流-按属性和最大值收集 [英] Stream - Collect by property and max
问题描述
给出以下类别(为问题简化):
Given the following class (simplified for the question):
public static class Match {
private final String type;
private final int score;
public Match(String type, int score) {
this.type = type;
this.score = score;
}
public String getType() {
return type;
}
public int getScore() {
return score;
}
}
我有一个Stream<Match>
,其中包含该类的多个实例,同一类型出现多次,但是得分不同:
I have a Stream<Match>
that contains multiple instances of the class, the same type appears multiple times, but with different scores:
Stream.of(new Match("A", 1), new Match("A", 2), new Match("A", 4), new Match("A", 10),
new Match("B", 3), new Match("B", 6), new Match("B", 12),
new Match("C", 1));
我现在想收集流,以使结果为List<Match>
,其中仅包含每种类型得分最高的实例.
I now want to collect the stream so that the result is a List<Match>
containing only the instances with the highest score of each type.
以下代码可以正常工作,但是我不确定它是否是最佳"解决方案(除了可怕的阅读和格式设置):
The following code is working, but I am not sure if it is the "optimal" solution (aside from the horrible reading and formatting):
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Match::getType, Collectors.collectingAndThen(
Collectors.toList(),
l -> l.stream().max(Comparator.comparing(Match::getScore)).get())), Map::values))
.forEach(m -> System.out.println(m.getType() + ": " + m.getScore()));
和:
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Match::getType, Collectors.maxBy(Comparator.comparing(Match::getScore))), Map::values))
.forEach(m -> m.ifPresent(ma -> System.out.println(ma.getType() + ": " + ma.getScore())));
输出(正确):
A:10
B:12
C:1
A: 10
B: 12
C: 1
此外,我无法提取返回收集器的通用静态方法,因此我可以按以下方式在需要的地方简单地使用它:
.collect(distinctMaxByProperty(Match::getType, Match::getScore)
Additionally I was not able to extract a generic, static method returning a collector so that I can simply use it where I need in a way like:
.collect(distinctMaxByProperty(Match::getType, Match::getScore)
任何帮助将不胜感激!
推荐答案
不要收集到List
中,仅当您可以首先收集最大元素(例如
Don’t collect into a List
, just to extract one value, when you can collect the maximum element in the first place, e.g.
Map<String,Match> result =
Stream.of(new Match("A", 1), new Match("A", 2), new Match("A", 4), new Match("A", 10),
new Match("B", 3), new Match("B", 6), new Match("B", 12), new Match("C", 1))
.collect(Collectors.groupingBy(Match::getType, Collectors.collectingAndThen(
Collectors.reducing(BinaryOperator.maxBy(
Comparator.comparingInt(Match::getScore))),
Optional::get)));
但是,只要您遇到需要在groupingBy
上下文中提取Optional
的情况,就值得检查是否具有合并功能的toMap`可以给出更简单的结果:
But whenever you encounter the necessity to extract an Optional
in the context of groupingBy
, it’s worth checking whether toMap` with merge function can give a simpler result:
Map<String,Match> result =
Stream.of(new Match("A", 1), new Match("A", 2), new Match("A", 4), new Match("A", 10),
new Match("B", 3), new Match("B", 6), new Match("B", 12), new Match("C", 1))
.collect(Collectors.toMap(Match::getType, Function.identity(),
BinaryOperator.maxBy(Comparator.comparingInt(Match::getScore))));
拥有Map
后,您可以通过以下方式产生所需的输出
Once you have the Map
you can produce your desired output via
result.values().forEach(m -> System.out.println(m.getType() + ": " + m.getScore()));
但是,如果您不需要实际的Match
实例,则可以做得更简单:
But if you don’t need the actual Match
instances, you can do it even simpler:
Stream.of(new Match("A", 1), new Match("A", 2), new Match("A", 4), new Match("A", 10),
new Match("B", 3), new Match("B", 6), new Match("B", 12), new Match("C", 1))
.collect(Collectors.toMap(Match::getType, Match::getScore, Math::max))
.forEach((type,score) -> System.out.println(type + ": " + score));
这篇关于流-按属性和最大值收集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!