如何使用 Java Stream API 更新列表中的每个元素及其排序排名?

问题描述 投票:0回答:3

我对 Java 8 流有一个简单的需求。

我目前正在通过对列表进行排序,然后按索引设置排名属性来解决此问题。 有没有更好的方法通过使用 Java 8 流来解决这个问题?

public class Student {
    private String name;
    private int score;
    private int rank;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("jack", 98);
        Student s2 = new Student("jason", 86);
        Student s3 = new Student("john", 80);

        List<Student> studentList = Arrays.asList(s1, s2, s3);
        // how to set rank property by score in each student with stream?

    }
}
java list sorting java-stream
3个回答
3
投票

流元素索引不可用,这是有道理的,因为流是为了在没有其他元素上下文的情况下处理元素而构建的。

但是,你可以做到:

List<Student> studentList = Arrays.asList(s1, s2, s3);
AtomicInteger index = new AtomicInteger();
studentList.stream()
  .sorted(Comparator.comparing(s -> -s.score)) // sort highest first
  .forEachOrdered(s -> s.rank = index.getAndIncrement());

这个排名从零到n-1。要从 1 到 n 排名,请将

getAndIncrement
更改为
incrementAndGet


0
投票

流是对列表进行排序的合理(尽管不是唯一)选择。 然而,基于索引的变异操作(例如根据索引为每个元素设置属性)目前并不是流可以轻松且惯用地处理的。

因此,我建议不要使用流来解决该部分问题,而应使用现有的非流方法。

例如,下面使用流创建列表的排序副本,然后使用

ListIterator
设置每个学生的排名(假设
Student
中字段的标准 getter 和 setter):

List<Student> sorted = studentList.stream()
        .sorted(Comparator.comparing(Student::getScore).reversed())
        .toList();

for (ListIterator<Student> i = sorted.listIterator(); i.hasNext(); ) {
    Student next = i.next();
    next.setRank(i.previousIndex());
}

0
投票

基于索引的变异操作(例如根据索引为每个元素设置属性)目前不是 Java 可以使用当前永久(非预览)Stream API 轻松且惯用地处理的操作。

也就是说,JEP 461:流收集器 Java 22 预览语言功能可用于将元素流转换为包含这些元素及其索引的流。 然后可以对其进行迭代以设置每个项目的排名:

record RankedStudent(int rank, Student student) {
}

class CurrentIndex {
    int index = 0;
}

Gatherer<Student, CurrentIndex, RankedStudent> gatherer = Gatherer.ofSequential(
        CurrentIndex::new,
        Gatherer.Integrator.ofGreedy((state, element, downstream) ->
                downstream.push(new RankedStudent(state.index++, element)))
);

studentList.stream()
        .sorted(Comparator.comparing(Student::getScore).reversed())
        .gather(gatherer)
        .forEachOrdered(s -> s.student().setRank(s.rank()));

自定义收集器跟踪当前索引,并将每个元素映射到包含元素和索引的自定义

RankedStudent
类型。

这首先按分数(降序)对流进行排序,使用自定义收集器将每个元素映射到包含该元素及其索引的

IndexedElement
,然后使用
forEachOrdered
对每个项目进行操作。

Java文档

Gatherer

将输入元素流转换为输出元素流的中间操作,可以选择在到达上游末尾时应用最终操作。 […]

[…]

聚集操作的例子有很多,包括但不限于:将元素分组(窗口函数);对连续相似的元素进行去重;增量累加功能(前缀扫描);增量重新排序功能等。类

Gatherers
提供了常见收集操作的实现。

API说明:

A

Gatherer
由四个函数指定,这些函数协同工作来处理输入元素,可选择使用中间状态,并可选择在输入结束时执行最终操作。他们是:

Stream.gather(Gatherer<? super T,?,R> gatherer)

返回一个流,其中包含将给定收集器应用于该流的元素的结果。

Gatherer.ofSequential(initializer, integrator)

返回由给定

Gatherer
initializer
描述的新的连续
integrator

© www.soinside.com 2019 - 2024. All rights reserved.