【问题标题】:Convert Map<String, Object> to Map<String, Set<Object>> with filter and streams使用过滤器和流将 Map<String, Object> 转换为 Map<String, Set<Object>>
【发布时间】:2019-01-02 11:23:26
【问题描述】:

我想转换我的地图,如下所示:

{
  key="someKey1", value=Apple(id="1", color="green"),
  key="someKey2", value=Apple(id="2", color="red"),
  key="someKey3", value=Apple(id="3", color="green"),
  key="someKey4", value=Apple(id="4", color="red"),
}

到另一张地图,它将所有相同颜色的苹果放在同一个列表中:

{
  key="red", value=list={apple1, apple3},
  key="green", value=list={apple2, apple4},  
}

我尝试了以下方法:

Map<String, Set<Apple>> sortedApples = appleMap.entrySet()
    .stream()
    .collect(Collectors.toMap(l -> l.getColour, ???));

我在正确的轨道上吗?我应该为此任务使用过滤器吗?有没有更简单的方法?

【问题讨论】:

    标签: java lambda java-8 java-stream


    【解决方案1】:

    Collectors.groupingByCollectors.toMap 更适合此任务(尽管两者都可以使用)。

    Map<String, List<Apple>> sortedApples = 
        appleMap.values()
                .stream()
                .collect(Collectors.groupingBy(Apple::getColour));
    

    或者,将它们分组到Sets 使用:

    Map<String, Set<Apple>> sortedApples = 
        appleMap.values()
                .stream()
                .collect(Collectors.groupingBy(Apple::getColour,
                                               Collectors.mapping(Function.identity(),
                                                                  Collectors.toSet())));
    

    或者(正如青峰评论的那样):

    Map<String, Set<Apple>> sortedApples = 
        appleMap.values()
                .stream()
                .collect(Collectors.groupingBy(Apple::getColour, Collectors.toSet()));
    

    【讨论】:

      【解决方案2】:

      如果你想继续toMap,你可以得到如下结果:

      map.values()  // get the apples
         .stream() // Stream<Apple>
         .collect(toMap(Apple::getColour, // group by colour
                   v ->  new HashSet<>(singleton(v)), // have values as set of apples
                (l, r) -> {l.addAll(r); return l;})); // merge colliding apples by colour
      
      • 通过映射 values 而不是 entrySet 流式传输,因为我们不关心映射键。
      • Apple::getColourkeyMapper 函数,用于提取我们希望分组的“事物”,在本例中为 Apples 颜色。
      • v -&gt; new HashSet&lt;&gt;(singleton(v)) 是用于生成地图值的 valueMapper 函数
      • (l, r) -&gt; {l.addAll(r); return l;} 是当Apple 的颜色发生键冲突时用于合并两个HashSet 的合并函数。
      • 最后,生成的地图是Map&lt;String, Set&lt;Apple&gt;&gt;

      但使用groupingBytoSet 作为下游会更好:

      map.values().stream().collect(groupingBy(Apple::getColour, toSet()));
      
      • 通过映射values 而不是entrySet 流式传输,因为我们不关心映射键。

      • 通过提供的分类函数(即Apple::getColour)对Apple 进行分组,然后将值收集到一个Set 中,因此是toSet 下游收集器。

      • 最后,生成的地图是Map&lt;String, Set&lt;Apple&gt;&gt;

      简短、易读且惯用的方法。

      你也可以在没有流的情况下这样做:

      Map<String, Set<Apple>> res = new HashMap<>();
      map.values().forEach(a -> res.computeIfAbsent(a.getColour(), e -> new HashSet<>()).add(a));
      
      • 遍历映射 values 而不是 entrySet,因为我们不关心映射键。
      • 如果指定的键a.getColour() 尚未与值关联,则尝试使用给定的映射函数e -&gt; new HashSet&lt;&gt;() 计算其值并将其输入到映射中。然后我们将Apple 添加到结果集中。
      • 如果指定的键 a.getColour() 已经与值 computeIfAbsent 关联,则返回与其关联的现有值,然后我们在 HashSet 上调用 add(a) 以将 Apple 输入集合。李>
      • 最后,生成的地图是Map&lt;String, Set&lt;Apple&gt;&gt;

      【讨论】:

        【解决方案3】:

        您可以使用Collectors.groupingByCollectors.toSet()

        Map<String, Set<Apple>> sortedApples = appleMap.values() // Collection<Apple>
                .stream() // Stream<Apple>
                .collect(Collectors.groupingBy(Apple::getColour, // groupBy colour
                        Collectors.mapping(a -> a, Collectors.toSet()))); // collect to Set
        

        【讨论】:

          【解决方案4】:

          您已经问过如何使用流来做到这一点,但这里有另一种方式:

          Map<String, Set<Apple>> result = new LinkedHashMap<>();
          appleMap.values().forEach(apple -> 
              result.computeIfAbsent(apple.getColor(), k -> new LinkedHashSet<>()).add(apple));
          

          这使用Map.computeIfAbsent,它要么返回映射到该颜色的集合,要么将一个空的LinkedHashSet 放入映射中(如果尚未映射到该颜色),然后将苹果添加到集合中。

          编辑:我使用LinkedHashMapLinkedHashSet 来保留插入顺序,但可以分别使用HashMapHashSet

          【讨论】:

          • 不错! +1,最好迭代地图值,因为你没有对key做任何事情,即appleMap.values().forEach(apple -&gt; result.computeIfAbsent(apple.getColour(), k -&gt; new LinkedHashSet&lt;&gt;()).add(apple));
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-24
          • 2014-01-29
          • 1970-01-01
          • 2022-01-17
          • 2015-11-21
          • 1970-01-01
          相关资源
          最近更新 更多