The groupingBy
操作(或类似的事情)是不可避免的,Map
由该操作创建的还在操作期间用于查找分组键和查找重复项。但你可以将它与群元素的减少结合起来:
Map<String, Friend> uniqueFriendMap = friends.stream()
.collect(Collectors.groupingBy(Friend::uniqueFunction,
Collectors.collectingAndThen(
Collectors.reducing((a,b) -> friendMergeFunction(a,b)), Optional::get)));
地图的值已经是不同朋友的结果。如果你真的需要一个List
,您可以使用简单的 Collection 操作来创建它:
List<Friend> mergedFriends = new ArrayList<>(uniqueFriendMap.values());
如果第二个操作仍然让您烦恼,您可以将其隐藏在collect
手术:
List<Friend> mergedFriends = friends.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Friend::uniqueFunction, Collectors.collectingAndThen(
Collectors.reducing((a,b) -> friendMergeFunction(a,b)), Optional::get)),
m -> new ArrayList<>(m.values())));
由于嵌套收集器代表了一个归约(另请参见这个答案 https://stackoverflow.com/a/57042622/2711488), 我们可以用toMap
反而:
List<Friend> mergedFriends = friends.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(Friend::uniqueFunction, Function.identity(),
(a,b) -> friendMergeFunction(a,b)),
m -> new ArrayList<>(m.values())));
取决于是否friendMergeFunction
is a static
方法或实例方法,您可以替换(a,b) -> friendMergeFunction(a,b)
with DeclaringClass::friendMergeFunction
or this::friendMergeFunction
.
但请注意,即使在您最初的方法中,也可以进行一些简化。当您只处理 a 的值时Map
,您不需要使用entrySet()
,这需要你打电话getValue()
在每个条目上。您可以处理values()
首先。那么,你就不需要冗长的input -> { return expression; }
语法,如input -> expression
足够了。由于前面的分组操作的组不能为空,所以过滤步骤就被废弃了。所以你原来的方法看起来像:
Map<String, List<Friend>> uniqueFriendMap
= friends.stream().collect(Collectors.groupingBy(Friend::uniqueFunction));
List<Friend> mergedFriends = uniqueFriendMap.values().stream()
.map(group -> group.stream().reduce((a,b) -> friendMergeFunction(a,b)).get())
.collect(Collectors.toList());
这还不错。如前所述,融合操作不会跳过Map
创造是不可避免的。它只会跳过List
s 代表每个组,因为它将把它们减少为一个Friend
到位。