【问题标题】:Using lambda expressions for summing up member variables?使用 lambda 表达式对成员变量求和?
【发布时间】:2016-10-12 14:57:54
【问题描述】:

我有一个类似以下的课程。

public class Votes{
    String name;
    int likes;
    int dislikes;

    //constructors, getters and setters    
}

我有一个如下列表。

List<Votes> votesList;

假设我在上面的列表中填充了一些元素。我想声明一个在该列表中执行分组和求和操作的方法。

例如,假设我将列表中的以下元素作为该方法的input

votesList.add(new Votes("A", 10, 5));
votesList.add(new Votes("B", 15, 10));
votesList.add(new Votes("A", 20, 15));
votesList.add(new Votes("B", 10, 25));
votesList.add(new Votes("C", 10, 20));
votesList.add(new Votes("C", 0, 15));

该方法应输出带有以下元素的List&lt;Votes&gt;

("A", 30, 20),
("B", 25, 35),
("C", 10, 35)

有没有一种简单的方法可以在 Java8 中使用流、lambda 表达式来做到这一点?如果我只有一个 int 成员,我知道如何使用 collectors 来完成。

谁能解释一下我该如何解决这种情况?

【问题讨论】:

  • 您必须编写自己的收集器。 docs.oracle.com/javase/8/docs/api/java/util/stream/…
  • @MarkoTopolnik 如果您能详细说明答案,我将不胜感激。 :))
  • @MarkoTopolnik 您能否对我的回答发表评论。 :)) 有什么改进的方法吗?
  • 这不是一种纯粹的功能性方法,但谁在乎它是否有效。而且代码非常简单,优势很大。

标签: java multithreading lambda java-8 java-stream


【解决方案1】:

一种方法是使用groupingBy,结合reducing

Collection<Optional<Votes>> res =
     votesList.stream().collect(groupingBy(v -> v.name, reducing((v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes)))).values();

这将给你一个Collection&lt;Optional&lt;Votes&gt;&gt;,你可以通过使用collectingAndThen 收集器组合reducing 收集器和整理器函数Optional::get 来摆脱它,但这将开始看起来很难阅读。

因此,另一种选择是使用toMap,并在两个投票具有相同名称时合并它们:

Collection<Votes> res =
    votesList.stream().collect(toMap(v -> v.name,
                                     v -> v,
                                     (v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes))).values();

从那里您可以使用ArrayList 复制构造函数(通过将collectingAndThenm -&gt; new ArrayList&lt;&gt;(m.values()) 一起使用,或者将前面的表达式放入此复制构造函数的参数列表中)。

【讨论】:

  • 嗨,非常感谢。你能看看我的答案,如果有的话给cmets。 :))
【解决方案2】:

最后,我发现这是最简单的方法。 :))

Map<String, List<Votes>> grouped = voteCountList.stream().collect(Collectors.groupingBy(r->r.getName()));

List<Votes> collectedList = new ArrayList<>();

grouped.forEach((groupName, votes) -> collectedList.add(new Votes(groupName,
                votes.stream().collect(Collectors.summingInt(r->r.getLikes())),
                votes.stream().collect(Collectors.summingInt(r->r.getDislikes())))));

【讨论】:

    【解决方案3】:

    这应该可以帮助您入门,这并不是您想要的,因为它不会创建最终列表。

    Map<String, Votes> collected = votesList.stream().collect(Collectors.groupingBy(Votes::getName,
            collectingAndThen(reducing(
                    (originalVotes, newVotes) -> new Votes(originalVotes.getName(), originalVotes.getLikes() + newVotes.getLikes(), originalVotes.getDislikes() + newVotes.getDislikes()))
                    , Optional::get)));
    
    collected.forEach((key, value) -> {
        System.out.println(key + "," + value.getLikes() + "," + value.getDislikes());
    });
    

    【讨论】:

    • 嗨,非常感谢。你能看看我的答案,如果有的话给cmets。 :))
    【解决方案4】:

    这是使用自定义收集器的解决方案。

    测试代码:

    final List<Votes> votesList = new ArrayList<>();
    votesList.add(new Votes("A", 10, 5));
    votesList.add(new Votes("B", 15, 10));
    votesList.add(new Votes("A", 20, 15));
    votesList.add(new Votes("B", 10, 25));
    votesList.add(new Votes("C", 10, 20));
    votesList.add(new Votes("C", 0, 15));
    
    final List<Votes> totals = votesList.stream().collect(new VoteCollector());
    totals.forEach(votes -> {
        System.out.println(votes.getName() + "  " + votes.getLikes() + "  " + votes.getDislikes());
    });
    

    VoteCollector 类:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.EnumSet;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    import java.util.function.BiConsumer;
    import java.util.function.BinaryOperator;
    import java.util.function.Function;
    import java.util.function.Supplier;
    import java.util.stream.Collector;
    import java.util.stream.Collectors;
    
    public class VoteCollector implements Collector<Votes, Map<String, Votes>, List<Votes>> {
    
        @Override
        public Supplier<Map<String, Votes>> supplier() {
            return HashMap::new;
        }
    
        @Override
        public BiConsumer<Map<String, Votes>, Votes> accumulator() {
            return (map, votes) -> {
                final Votes mapVotes = map.get(votes.getName());
                if (mapVotes == null) {
                    map.put(votes.getName(), new Votes(votes.getName(), votes.getLikes(), votes.getDislikes()));
                } else {
                    mapVotes.setLikes(mapVotes.getLikes() + votes.getLikes());
                    mapVotes.setDislikes(mapVotes.getDislikes() + votes.getDislikes());
                }
            };
        }
    
        @Override
        public BinaryOperator<Map<String, Votes>> combiner() {
            return (map1, map2) -> {
                for (final Entry<String, Votes> map2Entry : map2.entrySet()) {
                    final Votes map1Votes = map1.get(map2Entry.getKey());
                    if (map1Votes == null) {
                        map1.put(map2Entry.getKey(), map2Entry.getValue());
                    }
                    else {
                        map1Votes.setLikes(map1Votes.getLikes() + map2Entry.getValue().getLikes());
                        map1Votes.setDislikes(map1Votes.getDislikes() + map2Entry.getValue().getDislikes());
                    }
                }
    
                return map1;
            };
        }
    
        @Override
        public Function<Map<String, Votes>, List<Votes>> finisher() {
            return map -> map.values().stream().collect(Collectors.toList());
        }
    
        @Override
        public Set<Characteristics> characteristics() {
            return Collections.emptySet();
        }   
    }
    

    输出是

    A  30  20
    B  25  35
    C  10  35
    

    Here 是学习编写收集器的众多好资源之一:

    【讨论】:

    • 您可以将整理器简化为map -&gt; new ArrayList&lt;&gt;(map.values())。此外,您可以使用Collector.of 使用四个函数构造一个Collector,而无需创建实现Collector 接口的新类。
    • @Todd 嗨,非常感谢。你能看看我的答案,如果有的话给cmets。 :))
    猜你喜欢
    • 1970-01-01
    • 2017-06-10
    • 2015-11-23
    • 1970-01-01
    • 2021-01-07
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多