Java流合并或减少重复的对象

时间:2017-01-09 01:09:08

标签: java list merge java-stream reduce

我需要通过将所有重复条目合并到一个对象中,从列表中生成一个可以有重复项的唯一好友列表 示例 - 将朋友从不同的社交Feed中提取并放入1个大列表中  1.朋友 - [姓名:" Johnny Depp",dob:" 1970-11-10",来源:" FB",fbAttribute:"。 "]
 2.朋友 - [姓名:" Christian Bale",dob:" 1970-01-01",来源:" LI",liAttribute:"。 "]
 3.朋友 - [姓名:" Johnny Depp",dob:" 1970-11-10",来源:" Twitter",twitterAttribute:"。 "]
 4.朋友 - [姓名:" Johnny Depp",dob:" 1970-11-10",来源:" LinkedIn",liAttribute:"。 "]
 5.朋友 - [姓名:" Christian Bale",dob:" 1970-01-01",来源:" LI",liAttribute:"。 "]

预期输出
 1.朋友 - [姓名:" Christian Bale",dob:" 1970-01-01",liAttribute:" ..",fbAttribute:&#34 ; ...",twitterAttribute:" .."]
 2.朋友 - [姓名:" Johnny Depp",dob:" 1970-11-10",liAttribute:" ..",fbAttribute:&#34 ; ...",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的情况下执行此操作。有什么建议吗?

2 个答案:

答案 0 :(得分:9)

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())));

但请注意,即使在原始方法中,也可以进行多种简化。如果您只处理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

答案 1 :(得分:2)

只需使用方法Collectors.frequency