创建多个Maps的entrySets视图

时间:2015-06-19 22:05:32

标签: java generics

我有一个curl_setopt($cHandler, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($cHandler, CURLOPT_SSL_VERIFYPEER, false); ,我想提供这些地图的输入集合的视图,即Iterable<Map<? extends K, ? extends V>>

我可以像这样构造一个新的Set<Map.Entry<K, V>>(演员触发未经检查的警告,但编译):

Set

但是,这需要复制集,并且不会与支持映射保持同步。我尝试过这样的事情:

ImmutableSet.Builder<Map.Entry<K, V>> builder = ImmutableSet.builder();
for(Map<? extends K, ? extends V> map : chain) {
  for(Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
    builder.add((Map.Entry<K, V>) e);
  }
}
return builder.build();

但这无法编译,错误:

Set<Map.Entry<K, V>> unionSet = ImmutableSet.of();
for(Map<? extends K, ? extends V> map : chain) {
  unionSet = Sets.union((Set<Map.Entry<K, V>>)map.entrySet(), unionSet);
}
return unionSet;

我期待一个类似的不安全的演员,但在这种情况下似乎是禁止的,大概是由于嵌套的泛型。

所以有两个问题:

  1. 为什么禁止这种演员表,但允许使用顶级通用的演员表?
  2. 是否有更好的方法来提供这些集合的联合视图(类型为Cannot cast from Set<Map.Entry<capture#27-of ? extends K, capture#28-of ? extends V>> to Set<Map.Entry<K,V>> )?

2 个答案:

答案 0 :(得分:0)

这在技术上可以使用不安全的原始投射

Set<Map.Entry<K, V>> unionSet = ImmutableSet.of();
for(Map<? extends K, ? extends V> map : chain) {
  unionSet = Sets.union((Set) map.entrySet(), unionSet);
}

......这几乎可以肯定是提供入口集联合的实时视图的最佳方式。

至于为什么不允许通用论证的演员,我怀疑Set<Map.Entry<? extends K, ? extends V>>Set<Map.Entry<K, V>>之间在技术上没有子类型关系。对于冗长但技术上不同的Set<? extends Map.Entry<? extends K, ? extends V>>,它可能有所不同,这在技术上是Set<Map.Entry<K, V>>的概括,而另一个则不是。{/ p>

答案 1 :(得分:0)

您可以通过执行双重转换而不使用原始类型来编译您的不安全转换,例如:

static <K, V> Set<Map.Entry<K, V>> unionSet(Iterable<Map<? extends K, ? extends V>> maps) {
    Set<Map.Entry<K, V>> unionSet = ImmutableSet.of();
    for (Map<? extends K, ? extends V> map : maps) {
        @SuppressWarnings("unchecked") // unsafe! see below
        Set<Map.Entry<K, V>> castEntrySet = (Set<Map.Entry<K, V>>) (Set<?>) map.entrySet();
        unionSet = Sets.union(castEntrySet, unionSet);
    }
    return unionSet;
}

但是generic types aren't covariant for a reason。以下是一个不安全原因的例子:

Map<String, Integer> intMap = new HashMap<>();
intMap.put("key", 42);

Set<Map.Entry<String, Number>> unionSet = unionSet(ImmutableList.of(intMap));

Iterables.getOnlyElement(unionSet).setValue(0.42);

// java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Integer
Integer shouldBeAnInt = intMap.get("key");