我想转换如下所示的地图:
{
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, ???));
我走对了吗?我应该为此任务使用过滤器吗?有更容易的方法吗?
答案 0 :(得分:14)
Collectors.groupingBy
比Collectors.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
上流,因为我们不关心地图键。entrySet
是Apple::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.groupingBy
和Collectors.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>
编辑:我正在使用LinkedHashMap
和LinkedHashSet
来保留插入顺序,但可能分别使用了HashMap
和HashSet
。