使用过滤器和流将Map <string,object =“”>转换为Map <string,set <object =“” >>

时间:2019-01-02 11:23:27

标签: java lambda java-8 java-stream

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

{
  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, ???));

我走对了吗?我应该为此任务使用过滤器吗?有更容易的方法吗?

4 个答案:

答案 0 :(得分:14)

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

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

或者,将它们分组为Set,请使用:

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

或(如Aomine所说):

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

答案 1 :(得分:10)

如果您要继续使用.map(n => year.atDay(n)),则可以得到如下结果:

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上流,因为我们不关心地图键。
  • entrySetApple::getColour函数,用于提取我们希望根据keyMapper的颜色分组的“事物”。
  • Apple是用于生成地图值的v -> new HashSet<>(singleton(v))函数
  • valueMapper是合并功能,用于在(l, r) -> {l.addAll(r); return l;}的颜色发生键冲突时合并两个HashSet
  • 最后,生成的地图是Apple

,但最好使用Map<String, Set<Apple>>groupingBy作为下游:

toSet
  • 在地图map.values().stream().collect(groupingBy(Apple::getColour, toSet())); 而不是values上流过,因为我们不关心地图键。

  • 通过提供的分类功能(即entrySet)对Apple进行分组,然后在Set中收集值,因此在Apple::getColour下游收集器中。

  • 最后,生成的地图是toSet

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

您也可以在没有流的情况下进行操作:

Map<String, Set<Apple>>
  • 在地图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 -> new HashSet<>()添加到结果集中。
  • 如果指定的键Apple已经与值a.getColour()相关联,则返回与之关联的现有值,然后我们在computeIfAbsent上调用add(a)输入{ {1}}进入集合。
  • 最后,生成的地图是HashSet

答案 2 :(得分:8)

您可以使用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

答案 3 :(得分:3)

您已经问过如何使用流,但这是另一种方式:

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

这使用Map.computeIfAbsent,它返回映射到该颜色的集合,或者如果没有映射到该颜色的对象,则将一个空白的LinkedHashSet放入映射中,然后将苹果添加到该集合中。 / p>

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