使用Java Stream从ArrayList中获得最高分

时间:2019-01-27 15:42:37

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

我想按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(',');

4 个答案:

答案 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

现在使用使用一些收集器。

  • Collectors::groupingBy将所有Personid分组并在值的下游
  • 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借用了样板代码来设置以下演示:

Demo

(演示仅用于演示目的)

答案 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;
    }

}

}