Java 8 使用groupingBy&;扩展流的输出;Java8中的summingit

Java 8 使用groupingBy&;扩展流的输出;Java8中的summingit,java-8,java-stream,Java 8,Java Stream,我有一个话题: @Entity public class Topic { @Id private int id; private LocalDate date; private String name; private int points; @JoinColumn(name = "user_id", nullable = false) private User user; } 我通过spring data jpa方法获得给定日期内的主题

我有一个话题:

@Entity
public class Topic {
    @Id
    private int id;
    private LocalDate date;
    private String name;
    private int points;
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}
我通过spring data jpa方法获得给定日期内的主题列表:

List topics=topicRepository.findByDateBetween(开始,结束); 在输出上有哪些功能,例如:

Topic(id=1, date="2018-01-01", name="Java examples", User(...), 12)
Topic(id=2, date="2018-02-02", name="Java examples", User(...), 34)
Topic(id=3, date="2018-02-02", name="Java examples", User(...), 56)
Topic(id=4, date="2018-03-03", name="Java examples", User(...), 78)
Topic(id=5, date="2018-03-03", name="Java examples", User(...), 90)
我试图实现的是过滤我的结果输出(如果日期和用户是相同的添加点)

我的实际解决方案返回的映射以日期为键,汇总点为值,但我需要在输出中提供更多数据,如用户名或名称

return topics.stream()
        .collect(Collectors.groupingBy(Topic::getDate,
                Collectors.summingInt(Topic::getPoints)));
也许有另一种方法来代替映射返回为这种情况创建的dto?e、 g

@Data
public class ResultDto {
    private LocalDate date;
    private String name;
    private int points;
    private User user;
}

假设
User
一致地实现
hashCode
等于
,则可以将日期/用户组合键和
ResultDto
值分组到映射。为此,您需要两个操作:一个是将
映射到
,另一个是为每个组聚合点。(您可以将这些操作放在
ResultDto
或实用程序类中,等等,这里我假设第一个操作在
Mapper
实用程序类中,第二个操作在
ResultDto
中):

ResultDto
中:

public final class Mapper {

    private Mapper() { }

    public static ResultDto fromTopic(Topic topic) {
        ResultDto result = new ResultDto();
        result.setDate(topic.getDate());
        result.setName(topic.getName());
        result.setPoints(topic.getPoints());
        result.setUser(topic.getUser());
        return result;
    }
}
public ResultDto merge(ResultDto another) {
    this.points += another.points;
    return this;
}
请注意,我正在分配
Mapper.fromTopic
映射操作中找到的第一个
Topic.name
。我假设这在您的示例中是不一致的,如果您在实际场景中使用此方法,请将其考虑在内

现在,我们可以按日期/用户对主题和组进行流式处理:

Map<List<Object>, ResultDto> groups = topics.stream()
    .collect(Collectors.toMap(
        topic -> Arrays.asList(topic.getDate, topic.getUser()), // Java 9: List.of
        Mapper::fromTopic,
        ResultDto::merge));
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;

Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic, Integer> pointTotals = topics.stream()
        .collect(groupingBy(
                topic -> topic, 
                () -> new TreeMap<>(byDateAndUser), 
                summingInt(Topic::getPoints)
        ));

编辑:这里有一个没有流的更简洁的变体:

Map<List<Object>, ResultDto> groups = new HashMap<>();

topics.forEach(topic -> groups.merge(
    List.of(topic.getDate(), topic.getUser()), // Java 8: Arrays.asList
    Mapper.fromTopic(topic),
    ResultDto::merge));

Collection<ResultDto> results = groups.values();
Map groups=newhashmap();
topics.forEach(topic->groups.merge(
List.of(topic.getDate(),topic.getUser()),//Java 8:Arrays.asList
Mapper.fromTopic(主题),
结果:合并);
收集结果=groups.values();

按字段子集进行分组的一种简单方法是使用带有自定义
比较器的
树形图。假设你要定义

Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic,...> map = new TreeMap<>(byDateAndUser);

用户是否也需要相同?我的意思是,您是否需要按日期和用户分组,即如果对于
2018-02-02
您有2个用户,
user 1
有10和5,
user 2
有20和8,您将有两个组,
(2018-02-02,user 1)
有15和
(2018-02-02,user 2)
有28?是的,我给出了一个例子,当用户是相同的时候会发生什么。但你是对的,这就像你写的。好吧,那名字呢?你也需要按那个分组吗?如果不是,它不应该是
ResultDto
的属性(或者此dto应该有一个组成该组的所有名称的列表)。您的权利。不应该。但我们可以假设,我们也需要这个。谢谢@Federico。不幸的是,我需要离开。我会核对答案并尽快接受(可能明天早上)。再次感谢,祝你今天愉快。
Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic,...> map = new TreeMap<>(byDateAndUser);
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;

Comparator<Topic> byDateAndUser = Comparator.comparing(Topic::getDate)
            .thenComparing(t -> t.getUser().getUserId());

Map<Topic, Integer> pointTotals = topics.stream()
        .collect(groupingBy(
                topic -> topic, 
                () -> new TreeMap<>(byDateAndUser), 
                summingInt(Topic::getPoints)
        ));