2015-06-19 62 views
2

的视图我有一个Iterable<Map<? extends K, ? extends V>>,我想提供这些地图的入口集合的视图,即Set<Map.Entry<K, V>>创建多个地图'entrySets

我可以构建一个新的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; 

但这无法编译,出现错误:

Cannot cast from Set<Map.Entry<capture#27-of ? extends K, 
capture#28-of ? extends V>> to Set<Map.Entry<K,V>> 

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

所以两个问题:

  1. 为什么这种铸造的禁止,但顶级通用的类型转换被允许?
  2. 有没有更好的方法来提供这些集合的联合(类型Set<Map.Entry<K, V>>)?

回答

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>>的推广,而其他不是。

+0

那么,或者使用'Set <?扩展Map.Entry <?延伸K,?扩展V >>'。或者在整个代码中使用'Map '。 –

+0

我实际上已经完成并清理了'chain',它是'Iterable >';构造函数现在有一个不安全的强制转换,但其余的类更清晰。不过,在我重构它之前,原始的演员工作。 – dimo414

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"); 
+0

双投技巧有什么好处?看到第一个演员只是完全清除了泛型,我没有看到比原始演员更有优势。关于'Entry.setValue()'的好处,我一定会让'unionSet()'返回不可变的'Entry'。 – dimo414

+0

优点是避免必须抑制附加的原始类型警告。相反,您需要'@SuppressWarnings({“unchecked”,“rawtypes”})''。就我个人而言,我尽可能地避免使用原始类型,因为它们的使用有时会以惊人的方式消除其他泛型。 –