合并Map<String, List<String>> Java 8流

20

我想使用JAVA 8 Stream合并两个Map:

Map<String, List<String>> mapGlobal = new HashMap<String, List<String>>();
Map<String, List<String>> mapAdded = new HashMap<String, List<String>>();

我尝试使用这个实现:

mapGlobal = Stream.of(mapGlobal, mapAdded)
                .flatMap(m -> m.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                        Collectors.mapping(Map.Entry::getValue,        
                                           Collectors.toList())
                ));
然而,这种实现只会创建一个类似于:Map<String, List<Object>>的结果。如果一个键不在mapGlobal中,则会将其作为新的键添加,并与对应的字符串列表一起添加。如果键在mapGlobalmapAdded中都有重复,则两个值列表将合并为:A = {1,3,5,7}B = {1,2,4,6},然后A ∪ B = {1,2,3,4,5,6,7}
6个回答

23
您可以通过遍历mapAdded中的所有条目并将它们合并到mapGlobal中来完成此操作。
以下内容通过调用forEach(action)迭代mapAdded的条目,其中操作消耗每个条目的键和值。对于每个条目,我们在mapGlobal上调用merge(key, value, remappingFunction):如果键不存在,则会创建具有键k和值v的条目;如果键已存在,则会调用给定的重映射函数。此函数将要合并的2个列表作为参数,这些列表在本例中首先添加到TreeSet中以确保元素是唯一且排序的,然后再转换回列表形式。
mapAdded.forEach((k, v) -> mapGlobal.merge(k, v, (v1, v2) -> {
    Set<String> set = new TreeSet<>(v1);
    set.addAll(v2);
    return new ArrayList<>(set);
}));

如果您希望潜在地并行运行它,可以通过获取 entrySet() 并在其上调用 parallelStream() 来创建流式管道。但是,您需要确保使用支持并发的映射来处理 mapGlobal,例如 ConcurrentHashMap

ConcurrentMap<String, List<String>> mapGlobal = new ConcurrentHashMap<>();
// ...
mapAdded.entrySet().parallelStream().forEach(e -> mapGlobal.merge(e.getKey(), e.getValue(), (v1, v2) -> {
    Set<String> set = new TreeSet<>(v1);
    set.addAll(v2);
    return new ArrayList<>(set);
}));

这个实现不会使用流集合的改进。两个映射可能非常大,因此我想使用类似parallelStream()的东西。这可行吗? - ypriverol
2
除非“mapGlobal”是“ConcurrentMap”,否则在并行流中从内部更改它是不安全的。 - Misha
@Misha 正确,因此建议使用 ConcurrentHashMap - Tunaki
1
@Tunaki 对不起,我的阅读理解能力有问题,我自己扣一分。 - Misha
1
@Misha 我明白 :) 我做了一个编辑,让它更清晰。 - Tunaki

5

使用foreach遍历Map可以用来合并给定的ArrayList。

    public Map<String, ArrayList<String>> merge(Map<String, ArrayList<String>> map1, Map<String, ArrayList<String>> map2) {
    Map<String, ArrayList<String>> map = new HashMap<>();
    map.putAll(map1);

    map2.forEach((key , value) -> {
        //Get the value for key in map.
        ArrayList<String> list = map.get(key);
        if (list == null) {
            map.put(key,value);
        }
        else {
            //Merge two list together
            ArrayList<String> mergedValue = new ArrayList<>(value);
            mergedValue.addAll(list);
            map.put(key , mergedValue);
        }
    });
    return map;
}

2
原始实现不会生成像 Map<String, List<Object>> 这样的结果,而是生成 Map<String, List<List<String>>>。你需要在其上添加额外的 Stream 流水线来生成 Map<String, List<String>>

1
Map<String, List<String>> result = new HashMap<String, List<String>>();

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

for(Map.Entry<String, List<String>> entry: map1.entrySet()) {
   result.put(entry.getKey(), new ArrayList<>(entry.getValue());
}

for(Map.Entry<String, List<String>> entry: map2.entrySet()) {
   if(result.contains(entry.getKey())){
       result.get(entry.getKey()).addAll(entry.getValue());
   } else {
       result.put(entry.getKey(), new ArrayList<>(entry.getValue());
   }

}

该解决方案创建了一个独立的结果映射,没有任何与map1和map2列表的关联。


0

这里是迭代两个HashMap的完整代码,它们的值以列表形式存储。将第一个HashMap中的所有键和值合并。以下是示例。

HashMap<String, List<String>> hmap1 = new HashMap<>();
      List<String> list1 = new LinkedList<>();
      list1.add("000");
      list1.add("111");
      List<String> list2 = new LinkedList<>();
      list2.add("222");
      list2.add("333");
      hmap1.put("Competitor", list1);
      hmap1.put("Contractor", list2);
      //  System.out.println(hmap1);


      HashMap<String, List<String>> hmap2 = new HashMap<>();
      List<String> list3 = new LinkedList<>();
      list3.add("aaa");
      list3.add("bbb");
      List<String> list4 = new LinkedList<>();
      list4.add("ccc");
      list4.add("ddd");
      hmap2.put("Competitor", list3);
      hmap2.put("Contractor", list4);


//******* Java 8 Feature *****
hmap1.forEach((k, v) -> hmap2.merge(k, v, (v1, v2) -> {
          List<String> li = new LinkedList<>(v1);
          li.addAll(v2);
          hmap2.put(k,li);
          return new ArrayList<>(li);
      }));
      System.out.println(hmap2);

输出:

{Competitor=[aaa, bbb, 000, 111], Contractor=[ccc, ddd, 222, 333]}

0

使用StreamEx

Map<String, List<String>> mergedMap =
        EntryStream.of(mapGlobal)
                .append(EntryStream.of(mapAdded))
                .toMap((v1, v2) -> {
                    List<String> combined = new ArrayList<>();
                    combined.addAll(v1);
                    combined.addAll(v2);
                    return combined;
                });

如果您有更多的地图需要合并,只需将其附加到流中即可。
                .append(EntryStream.of(mapAdded2))
                .append(EntryStream.of(mapAdded3))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接