使用lambda表达式来总结成员变量?

时间:2016-10-12 14:57:54

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

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

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<Votes>

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

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

有人可以解释一下我该如何处理这种情况?

4 个答案:

答案 0 :(得分:3)

一种方法是使用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<Optional<Votes>>,你可以通过使用reducing收集器组合Optional::get收集器和终结器函数collectingAndThen来摆脱它,但这将开始看起来很难读。

所以另一种选择可能是使用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 -> new ArrayList<>(m.values()),或者将前一个表达式放在这个拷贝构造函数的参数列表中。) / p>

答案 1 :(得分:3)

最后,我认为这是最简单的方法。 :))

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

答案 2 :(得分:2)

这应该让你开始,它不是你想要的,因为它不会创建最终列表。

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

答案 3 :(得分:2)

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

测试代码:

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是学习编写收藏家的众多好来源之一: