合并馆藏地图

时间:2019-02-08 21:38:31

标签: java generics

我试图更好地理解Java中的泛型,因此编写了一个合并两个集合图的泛型方法。 (在创建硬编码的ArrayList的那一刻,请忽略它。)

public static <E, K> void  mergeMaps(Map<K, Collection<E>> receivingMap, Map<K, Collection<E>> givingMap) {
    for (Map.Entry<K, Collection<E>> entry : givingMap.entrySet()) {
        Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<E>());
        someCollection.addAll(entry.getValue());
    }
}

我的目标是mergeMaps函数能够合并其值可以是任意集合(ArrayListLinkedHashMap,...)的地图(相同类型)。< / p>

但是,当我尝试合并Map<Integer, ArrayList<String>>的两个实例时,遇到了编译时错误,但我不太理解编译器在告诉我什么。

public static void main(String[] args) {
    Map<Integer, ArrayList<String>> map1 = new HashMap<>();
    Map<Integer, ArrayList<String>> map2 = new HashMap<>();
    mergeMaps(map1, map2); // <-- compile error
}

这有什么问题,我该如何解决?

Error:(9, 9) java: method mergeMaps in class CollectionUtil cannot be applied to given types;
  required: java.util.Map<K,java.util.Collection<E>>,java.util.Map<K,java.util.Collection<E>>
  found: java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>>,java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>>
  reason: cannot infer type-variable(s) E,K
    (argument mismatch; java.util.Map<java.lang.Integer,java.util.ArrayList<java.lang.String>> cannot be converted to java.util.Map<K,java.util.Collection<E>>)

2 个答案:

答案 0 :(得分:3)

方法的签名是

       select 
        Dateadd(s,convert(Bigint,1538397000000/1000,convert(datetime,'1-1-1970'))

然后使用<E, K> void mergeMaps(Map<K, Collection<E>> receivingMap, Map<K, Collection<E>> givingMap) 作为参数类型的调用是无效的,因为Map<Integer, List<String>>不是Collection方法的通用类型参数。
为什么会出现问题?对于泛型,mergeMaps无法分配给Map<Integer, List<String>>变量(或以这种方式作为方法参数传递)。这是因为通用类型是不变的(有关更多信息,请参见here。简而言之,这意味着Map<Integer, Collection<String>>不一定与任何List<Integer>兼容,尽管List<Number>ArrayList<Number>)。

换句话说,具体参数必须为List<Number>类型。这是您的第一个解决方案:

Map<Integer, Collection<String>>

如果要允许使用类型为//Solution 1: change your arguments to Map<Integer, Collection<String>>: Map<Integer, Collection<String>> map1 = new HashMap<>(); Map<Integer, Collection<String>> map2 = new HashMap<>(); mergeMaps(map1, map2); 的参数进行调用,则必须更改目标方法以在映射值周围引入通用参数:

Map<Integer, List<String>>

这可以在将值声明为public static <E, K, C extends Collection<E>> void mergeMaps2(Map<K, C> receivingMap, Map<K, C> givingMap) { for (Map.Entry<K, C> entry : givingMap.entrySet()) { Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>()); someCollection.addAll(entry.getValue()); } } 的子类型的映射中调用(只要两个参数中的Collection<E>类型相同)即可。

Collection

旁注(或题外话)

现在,当您对此进行编译时,您还有另一个问题:此行上有一个编译器警告:

Map<Integer, List<String>> map1 = new HashMap<>();
Map<Integer, List<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);

Map<Integer, Set<String>> map1 = new HashMap<>();
Map<Integer, Set<String>> map2 = new HashMap<>();
mergeMaps2(map1, map2);

声称Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>()); 是未经检查的演员表。为什么这个?让我们看一下上面的示例调用(我建议将两个示例相加):

电话1:

(C) new ArrayList<E>()

在此示例中,Map<Integer, List<String>> map1 = new HashMap<>(); Map<Integer, List<String>> map2 = new HashMap<>(); mergeMaps2(map1, map2); 意味着将receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>())的实例作为值添加到映射。由于实际对象 的类型与调用者的声明类型(ArrayList<String>)兼容,因此可以。

现在,您认为这会做什么?

电话2:

List<String>

在这种情况下,不幸的是Map<Integer, Set<String>> map1 = new HashMap<>(); Map<Integer, Set<String>> map2 = new HashMap<>(); mergeMaps2(map1, map2); 仍将尝试添加一个receivingMap.computeIfAbsent(entry.getKey(), k -> (C) new ArrayList<E>()),它恰巧与呼叫者的期望值类型({{1} }。

在具体类型参数的上下文中,编译器不能确定强制转换ArrayList<String>始终是正确的。它放弃了,但发出警告以警告开发人员。

处理这个问题实际上是一个棘手的问题。您需要知道要实例化的类型,但是您的方法参数将不允许您这样做,因为您不能仅运行Set<String>。您自己的要求和设计将确定正确的解决方案,但我将以一种可能的解决方案结束:

(C) new ArrayList<E>()

答案 1 :(得分:0)

错误显示java.util.List<java.lang.String>> cannot be converted to java.util.Map<K,java.util.Collection<E>>)

您必须修改您的方法:

public static <E, K> void  mergeMaps(Map<K, List<E>> receivingMap, Map<K, List<E>> givingMap) {
    for (Map.Entry<K, List<E>> entry : givingMap.entrySet()) {
        Collection<E> someCollection = receivingMap.computeIfAbsent(entry.getKey(), k -> new ArrayList<E>());
        someCollection.addAll(entry.getValue());
    }
}