使用过滤器和流将Map <String,Object>转换为Map <String,Set <Object >>

M4V*_*V3N 20 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"),
}
Run Code Online (Sandbox Code Playgroud)

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

{
  key="red", value=list={apple1, apple3},
  key="green", value=list={apple2, apple4},  
}
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法:

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

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

Era*_*ran 14

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

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

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

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

或(如Aomine评论):

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


Ous*_* D. 11

如果你想继续,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
Run Code Online (Sandbox Code Playgroud)
  • 流过地图values而不是entrySet因为我们不关心地图键.
  • Apple::getColourkeyMapper用于提取我们希望分组的"东西" 的函数,在这种情况下是Apples颜色.
  • v -> new HashSet<>(singleton(v))valueMapper用于生成的映射值的函数
  • (l, r) -> {l.addAll(r); return l;}合并函数用于HashSetApple颜色上存在键碰撞时组合两个.
  • 最后,得到的地图是一个 Map<String, Set<Apple>>

但这是更好的groupingBytoSet下游:

map.values().stream().collect(groupingBy(Apple::getColour, toSet()));
Run Code Online (Sandbox Code Playgroud)
  • 流过地图values而不是entrySet因为我们不关心地图键.

  • Apple通过提供的分类函数对其进行分组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));
Run Code Online (Sandbox Code Playgroud)
  • 迭代地图values而不是entrySet因为我们不关心地图键.
  • 如果指定的键a.getColour()尚未与值关联,则尝试使用给定的映射函数计算其值e -> new HashSet<>()并将其输入到映射中.然后我们将其添加Apple到结果集中.
  • 如果指定的键a.getColour()已经与某个值computeIfAbsent相关联,则返回与之关联的现有值,然后我们调用add(a)HashSet来进入Apple该集合.
  • 最后,得到的地图是一个 Map<String, Set<Apple>>


Nam*_*man 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
Run Code Online (Sandbox Code Playgroud)