我想按ID来获得最高得分组。如果两个最高得分相同,那么我想根据最低的Optional ID获得最高得分。我想在Java Stream中获得它。到目前为止,我正在尝试以下代码不起作用 示例:
数组列表:
ID:1得分:80可选ID:1
ID:1得分:90可选Id:2
ID:1得分:90可选Id:3
ID:2得分:80可选Id:1
ID:2得分:100可选ID:3
ID:2得分:100 OptionalId:5
结果应为
ID:1分90可选Id:2
ID 2得分100可选Id:3
Map<Long, Optional<Person>> result1 = records.stream()
.collect(Collectors.groupingBy(Person::getId,
Collectors.maxBy(Comparator.comparing(Person::getScore)),
Collector.minBy(Comparator.comparing(Person::getOptionalId))));
for(Person ns: result1) {
sb.append(ns.getBatchNumber());
sb.append(',');
答案 0 :(得分:3)
我建议您从自定义Comparator<Person>
开始,该自定义优先级为score
的最大值和随后的optionalId
的最小值。为了简洁起见,这是将其传递给变量的好方法:
final Comparator<Person> comparator = Comparator
.comparing(Person::getScore) // descending score first
.thenComparing(Comparator // then ..
.comparing(Person::getOptionalId) // .. optionalId
.reversed()); // .. but ascending
现在使用java-stream使用一些收集器。
Collectors::groupingBy
将所有Person
按id
分组并在值的下游Collectors::reducing
使用Person
将具有相同id
的所有Comparator<Person>
减少为一个score
并获得最低的{ {1}}。Collectors::collectingAndThen
将结构从optionalId
展平为Collection<Optional<Person>>
,因为任何归约运算都将生成为Optional
-此步骤是可选的,根据您的示例可以跳过。 / li>
代码如下:
Collection<Person>
[人员[id = 1,得分= 90,可选ID = 2],人员[id = 2,得分= 100,可选ID = 3]]
答案 1 :(得分:2)
对于给定的ID值,必须有一个Person。 Id值的存在仅取决于人员。因此,如果存在ID,那么也必须有一个Person。因此,将Optional<Person>
作为映射的值有什么意义。相反,仅将Person
实例作为map
中的值会更有意义。在这里,我与toMap
配合使用BinaryOperator.maxBy
收集器来完成工作。这是它的外观。请注意如何将BinaryOperator.maxBy
用作mergeFunction
。
Map<Integer, Person> maxPersonById = records.stream()
.collect(Collectors.toMap(Person::getId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Person::getScore)
.thenComparing(Comparator.comparing(Person::getOptionalId).reversed()))));
这是上面给定输入的输出。
{1=Person [id=1, score=90, optionalId=2], 2=Person [id=2, score=100, optionalId=3]}
答案 2 :(得分:1)
您可以尝试使用以下按ID
进行汇总的流代码,然后使用两级排序来找到最高分,首先是按得分,然后是可选ID(如果得分相同):>
import static java.util.Collections.reverseOrder;
import static java.util.Comparator.comparing;
Map<Long, Optional<Person>> result1 = records.stream()
.collect(Collectors.groupingBy(Person::getId,
Collectors.maxBy(
Comparator.comparing(Person::getScore)
.thenComparing(reverseOrder(comparing(Person::getOptionalId))))));
Optional[ID: 1 Score: 90 OptionalId: 2]
Optional[ID: 2 Score: 100 OptionalId: 3]
这里的技巧是反转仅 可选ID的排序顺序,该ID我们要升序而不是降序。默认情况下,排序顺序为降序,因为我们正在调用Collections.maxBy
。
我引用this great SO question来获得有关反向语法的帮助。另外,我从@mayamar借用了样板代码来设置以下演示:
(演示仅用于演示目的)
答案 3 :(得分:0)
我做了些改动,并引入了一个可比较分数和optionalId的助手类。
public class T21Group {
public static void main(String[] args) {
List<Person> records = new ArrayList<>();
records.add(new Person(1, 80, 1));
records.add(new Person(1, 90, 2));
records.add(new Person(1, 90, 3));
records.add(new Person(2, 80, 1));
records.add(new Person(2, 100, 3));
records.add(new Person(2, 100, 5));
Map<Long, Optional<Person>> result1 = records.stream()
.collect(Collectors.groupingBy(Person::getId, Collectors.maxBy(Comparator.comparing(Pair::new))));
for (Optional<Person> ns : result1.values()) {
System.out.println(ns);
}
}
public static class Pair implements Comparable<Pair> {
long score;
long optionalId;
public Pair(Person p) {
score = p.getScore();
optionalId = p.getOptionalId();
}
@Override
public int compareTo(Pair o) {
if (this.score == o.score) {
return Long.compare(o.optionalId, this.optionalId);
}
return Long.compare(this.score, o.score);
}
}
public static class Person {
private long id;
private long score;
private long optionalId;
public Person(long id, long score, long optionalId) {
this.id = id;
this.score = score;
this.optionalId = optionalId;
}
@Override
public String toString() {
return "ID: " + id + " Score: " + score + " OptionalId: " + optionalId;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getScore() {
return score;
}
public void setScore(long score) {
this.score = score;
}
public long getOptionalId() {
return optionalId;
}
public void setOptionalId(long optionalId) {
this.optionalId = optionalId;
}
}
}