【问题标题】:Merging Map streams using Java 8 Lambda Expression使用 Java 8 Lambda 表达式合并 Map 流
【发布时间】:2015-10-07 21:55:37
【问题描述】:

我有两个类型为Map<Integer, String> 的地图m1m2,它们必须合并为一个地图 Map<Integer, List<String>>,其中两个映射中相同键的值被收集到一个列表中并放入一个新的映射中。

基于我探索的解决方案:

Map<Integer, List<String>> collated =
        Stream.concat(m1.entrySet().stream(), m2.entrySet().stream()).collect(
                Collectors.toMap(Entry::getKey,
                        Entry::getValue, (a, b) -> {
                            List<String> merged = new ArrayList<String>(a);
                            merged.addAll(b);
                            return merged;
                        }));

但是,此解决方案要求源 ListMap&lt;Integer, List&lt;String&gt;&gt;,因为 toMap 中的合并函数要求操作数和结果属于同一类型。

我不想更改源集合。请提供您对使用 lambda 表达式实现此目的的意见。

【问题讨论】:

    标签: java dictionary lambda java-8 java-stream


    【解决方案1】:

    为了清楚起见,您可以使用带有三个参数的Collectors.toMap 方法。

    Map<Integer, String> m1 = Map.of(1, "A", 2, "B");
    Map<Integer, String> m2 = Map.of(1, "C", 2, "D");
    
    // two maps into one
    Map<Integer, List<String>> m3 = Stream
            .of(m1.entrySet(), m2.entrySet())
            .flatMap(Collection::stream)
            .collect(Collectors.toMap(
                    // key - Integer
                    e -> e.getKey(),
                    // value - List<String>
                    e -> List.of(e.getValue()),
                    // mergeFunction - two lists into one
                    (list1, list2) -> {
                        List<String> list = new ArrayList<>();
                        list.addAll(list1);
                        list.addAll(list2);
                        return list;
                    }));
    
    // output
    System.out.println(m3); // {1=[A, C], 2=[B, D]}
    

    【讨论】:

      【解决方案2】:

      这似乎是使用 Guava 的 Multimap 的绝佳机会。

      ListMultimap<Integer, String> collated = ArrayListMultimap.create();
      collated.putAll(Multimaps.forMap(m1));
      collated.putAll(Multimaps.forMap(m2));
      

      如果你真的需要Map&lt;Integer, List&lt;String&gt;&gt;:

      Map<Integer, List<String>> mapCollated = Multimaps.asMap(collated);
      

      【讨论】:

        【解决方案3】:

        我现在无法对其进行测试,但我认为您只需要将值的映射从 Entry::getValue 更改为包含该值的列表:

        Map<Integer, List<String>> collated =
                Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
                        .collect(Collectors.toMap(Entry::getKey,
                                e -> {
                                    List<String> v = new ArrayList<String>();
                                    v.add(e.getValue());
                                    return v;
                                },
                                (a, b) -> {
                                    List<String> merged = new ArrayList<String>(a);
                                    merged.addAll(b);
                                    return merged;
                                }));
        

        编辑:这个想法是正确的。语法不是。当前的语法有效,虽然有点难看。一定有更短的写法。

        您也可以将e -&gt; {..} 替换为e -&gt; new ArrayList&lt;String&gt;(Arrays.asList(new String[]{e.getValue()}))

        e -&gt; Stream.of(e.getValue()).collect(Collectors.toList())

        或者你可以用groupingBy 来做:

        Map<Integer, List<String>> collated =
                Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
                        .collect(Collectors.groupingBy(Map.Entry::getKey,
                                Collectors.mapping(Map.Entry::getValue,
                                        Collectors.toList())));
        

        【讨论】:

          【解决方案4】:

          如果您想要纯 Java-8 解决方案,Misha 的解决方案是最好的。如果您不介意使用第三方库,使用我的StreamEx 会更短一些。

          Map<Integer, List<String>> map = StreamEx.of(m1, m2)
                  .flatMapToEntry(Function.identity())
                  .grouping();
          

          在内部它与 Misha 的解决方案相同,只是语法糖。

          【讨论】:

            【解决方案5】:

            这是groupingBycollector 的工作:

            Stream.of(m1,m2)
                    .flatMap(m->m.entrySet().stream())
                    .collect(groupingBy(
                            Map.Entry::getKey,
                            mapping(Map.Entry::getValue, toList())
                    ));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-11-16
              • 1970-01-01
              • 2018-03-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-10-20
              相关资源
              最近更新 更多