Java 流合并或减少重复对象
Java stream merge or reduce duplicate objects
我需要通过将所有重复的条目合并到一个对象中,从一个可以有重复项的列表中生成一个唯一的朋友列表
示例 - 从不同的社交提要中获取朋友并放入 1 个大列表
1.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"FB",fbAttribute:“..”]
2.朋友-[姓名:"Christian Bale",出生日期:“1970-01-01”,来源:"LI",liAttribute:“..”]
3.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"Twitter",twitterAttribute:“..”]
4.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"LinkedIn",liAttribute:“..”]
5.朋友-[姓名:"Christian Bale",出生日期:"1970-01-01",来源:"LI",liAttribute:".."]
预期输出
1. 好友 - [name: "Christian Bale", dob: "1970-01-01", liAttribute: "..", fbAttribute: "..", twitterAttribute: ".."]
2. 好友-[name:"Johnny Depp",dob:"1970-11-10",liAttribute:"..",fbAttribute:"..",twitterAttribute:".."]
问题 - 我如何在不使用任何中间容器的情况下进行合并?我可以轻松地使用中间映射并对条目的每个值应用 reduce。
List<Friend> friends;
Map<String, List<Friend>> uniqueFriendMap
= friends.stream().groupingBy(Friend::uniqueFunction);
List<Friend> mergedFriends = uniqueFriendMap.entrySet()
.stream()
.map(entry -> {
return entry.getValue()
.stream()
.reduce((a,b) -> friendMergeFunction(a,b));
})
.filter(mergedPlace -> mergedPlace.isPresent())
.collect(Collectors.toList());
我喜欢在不使用中间 Map uniqueFriendMap 的情况下执行此操作。有什么建议吗?
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())));
由于嵌套收集器代表一个 Reduction(另见 ),我们可以使用 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
是 static
方法还是实例方法,您可以将 (a,b) -> friendMergeFunction(a,b)
替换为 DeclaringClass::friendMergeFunction
或 this::friendMergeFunction
。
但请注意,即使在您原来的方法中,也可以进行一些简化。当你只处理一个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
的创建,因为它会将它们减少到单个 Friend
就地。
我需要通过将所有重复的条目合并到一个对象中,从一个可以有重复项的列表中生成一个唯一的朋友列表
示例 - 从不同的社交提要中获取朋友并放入 1 个大列表
1.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"FB",fbAttribute:“..”]
2.朋友-[姓名:"Christian Bale",出生日期:“1970-01-01”,来源:"LI",liAttribute:“..”]
3.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"Twitter",twitterAttribute:“..”]
4.朋友-[姓名:"Johnny Depp",出生日期:“1970-11-10”,来源:"LinkedIn",liAttribute:“..”]
5.朋友-[姓名:"Christian Bale",出生日期:"1970-01-01",来源:"LI",liAttribute:".."]
预期输出
1. 好友 - [name: "Christian Bale", dob: "1970-01-01", liAttribute: "..", fbAttribute: "..", twitterAttribute: ".."]
2. 好友-[name:"Johnny Depp",dob:"1970-11-10",liAttribute:"..",fbAttribute:"..",twitterAttribute:".."]
问题 - 我如何在不使用任何中间容器的情况下进行合并?我可以轻松地使用中间映射并对条目的每个值应用 reduce。
List<Friend> friends;
Map<String, List<Friend>> uniqueFriendMap
= friends.stream().groupingBy(Friend::uniqueFunction);
List<Friend> mergedFriends = uniqueFriendMap.entrySet()
.stream()
.map(entry -> {
return entry.getValue()
.stream()
.reduce((a,b) -> friendMergeFunction(a,b));
})
.filter(mergedPlace -> mergedPlace.isPresent())
.collect(Collectors.toList());
我喜欢在不使用中间 Map uniqueFriendMap 的情况下执行此操作。有什么建议吗?
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())));
由于嵌套收集器代表一个 Reduction(另见 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
是 static
方法还是实例方法,您可以将 (a,b) -> friendMergeFunction(a,b)
替换为 DeclaringClass::friendMergeFunction
或 this::friendMergeFunction
。
但请注意,即使在您原来的方法中,也可以进行一些简化。当你只处理一个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
的创建,因为它会将它们减少到单个 Friend
就地。