【问题标题】:Get Highest Score from ArrayList Using Java Stream使用 Java Stream 从 ArrayList 获得最高分
【发布时间】:2019-06-20 17:46:46
【问题描述】:

我想通过 Id 获得最高分组。如果两个最高分相同,那么我想根据最低可选 ID 获得最高分。我想在 Java Stream 中获得它。到目前为止我正在尝试以下代码不起作用 示例:

数组列表:

ID:1 得分:80 OptionalId:1
ID:1 得分:90 OptionalId:2
ID:1 得分:90 OptionalId:3
ID:2 得分:80 OptionalId:1
ID:2 得分:100 OptionalId:3
ID:2 分数:100 OptionalId:5

结果应该是

ID:1 分数 90 OptionalId:2
ID 2 得分 100 OptionalId: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(',');

【问题讨论】:

  • 80分的分数怎么了?
  • 80分应该淘汰。我只取每组的最高分。从上面的例子中,80是最低的,有两个90是最高的。两个之间我只取一个90可选 ID 最低的最高者。目前,max by 是从两个 90 年代随机取最高值,我不想要
  • @TimBiegeleisen:消失了。请注意应用于分数的Collectors.maxBy
  • OptionalId 真的是java.util.Optional?或int/long?

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


【解决方案1】:

我建议您从自定义Comparator&lt;Person&gt; 开始,它的优先级最高为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 将所有 Persons 按 id 和下游值分组
  • Collectors::reducing 将具有相同id 的所有Persons 减少为一个,使用Comparator&lt;Person&gt; 得到具有最高score 和最低optionalId 的一个。
  • Collectors::collectingAndThen 将结构从 Collection&lt;Optional&lt;Person&gt;&gt; 展平为 Collection&lt;Person&gt;,因为任何归约操作都会导致 Optional - 此步骤是可选的,可以根据您的示例跳过。

代码如下:

Collection<Person> filtered = records.stream()         // Stream<Person>
    .collect(Collectors.groupingBy(                    // from Map<Long, List<Person>>
        Person::getId,
        Collectors.collectingAndThen(                  // .. downstream to ..
                Collectors.reducing((a, b) ->          // .. Map<Long, Optional<Person>>
                    comparator.compare(a, b) > 0 ? a : b),
                Optional::get))                        // .. Map<Long, Person>
    .values();                                         // .. Collection<Person>

[人 [id=1, score=90, optionalId=2], 人 [id=2, score=100, optionalId=3]]

【讨论】:

  • 感谢您的宝贵建议。非常感谢。
【解决方案2】:

对于给定的 Id 值,必须有一个 Person。 Id 值的存在完全取决于 Person。因此,如果存在 Id,则也必须存在 Person。因此,将Optional&lt;Person&gt; 作为地图的值有什么意义。相反,仅将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]}

【讨论】:

  • 这是正确的答案,+1 表示 Collectors.toMapBinaryOperator.maxBy,这是最好的方法。
【解决方案3】:

您可以尝试以下按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的排序顺序进行反转,我们希望它是升序而不是降序。默认情况下排序顺序是降序,因为我们调用的是Collections.maxBy

我引用 this great SO question 来获取反向语法的帮助。另外,我从@mayamar 借用了样板代码来设置以下演示:

Demo

(demo仅用于演示目的)

【讨论】:

  • 感谢您的宝贵建议。非常感谢。
  • @TimBiegeleisen 这只有在您假设 OptionalId 真的是 long 时才有效,但它可能是 java.util.Optional...
  • @Eugene 我不解释 OP 使用“可选”一词来指代 Optional 类,而是认为辅助 ID 只是一个可选的数据片段,它可能或不得用于打破平局。
【解决方案4】:

我做了一点改动,引入了一个比较 score 和 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;
    }

}

}

【讨论】:

  • 你永远不会检查可选值,以防平分。
  • Grrrr。好吧,更改 Comparator 以使用该值也不难。我看看。
猜你喜欢
  • 1970-01-01
  • 2014-07-24
  • 1970-01-01
  • 2017-08-03
  • 1970-01-01
  • 1970-01-01
  • 2020-02-11
  • 1970-01-01
  • 2021-12-06
相关资源
最近更新 更多