如何使用Java Stream API为列表各元素设置排序后的对应排名
Java 8 Stream实现分数排名赋值方案
你原有「先排序再遍历设置rank」的逻辑是合理的,用Stream可以更简洁地实现,根据业务常用的两种排名规则,对应实现如下:
注意:运行代码前需要先给Student类补充各属性的getter/setter方法
方案1:顺序连续排名(无并列)
排名按排序后的位置从1开始连续递增,即使分数相同也按二级排序规则区分先后,适合不需要并列排名的场景。
import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; 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); List<Student> rankedList = studentList.stream() // 先按分数降序排序,同分可追加二级规则,比如按姓名升序 .sorted(Comparator.comparingInt(Student::getScore).reversed() .thenComparing(Student::getName)) .collect(Collectors.toList()); // 遍历排序后的列表按位置设rank IntStream.range(0, rankedList.size()) .forEach(i -> rankedList.get(i).setRank(i + 1)); } }
方案2:并列跳跃排名(同分同名次)
即业务中最常用的排名规则:相同分数的学生排名一致,后续排名跳过占用的名次,比如两个98分并列第1,下一个86分就是第3名。
因为Lambda表达式要求引用的外部变量为final或等效final,这里用原子类维护遍历过程中的状态:
import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { Student s1 = new Student("jack", 98); Student s2 = new Student("alice", 98); // 同分测试用例 Student s3 = new Student("jason", 86); Student s4 = new Student("john", 80); List<Student> studentList = Arrays.asList(s1, s2, s3, s4); // 维护三个状态:当前遍历序号、当前排名、上一个学生的分数 AtomicInteger seq = new AtomicInteger(0); AtomicInteger currentRank = new AtomicInteger(1); AtomicInteger lastScore = new AtomicInteger(Integer.MAX_VALUE); List<Student> rankedList = studentList.stream() .sorted(Comparator.comparingInt(Student::getScore).reversed()) .peek(stu -> { int curSeq = seq.incrementAndGet(); // 分数和上一个不一致时,更新排名为当前序号 if (stu.getScore() != lastScore.get()) { currentRank.set(curSeq); lastScore.set(stu.getScore()); } stu.setRank(currentRank.get()); }) .collect(Collectors.toList()); } }
补充说明
- 如果需要实现并列连续排名(即两个98分并列第1,下一个86分是第2名),只需要调整状态逻辑:额外维护一个同分计数,分数相同时计数+1,分数变化时用
当前排名 + 同分计数更新排名,同时重置计数即可。 - 以上实现直接修改原
Student对象的rank属性,如果不想改动原列表元素,可以在流的map阶段新建Student对象,拷贝属性并设置rank后返回新对象。 - 十万级以下数据量场景下,Stream实现和普通for循环性能差异可忽略;超大数据量场景下普通for循环性能略优,可按需选择。
内容的提问来源于stack exchange,提问作者Kano2525
相关产品推荐
相关产品推荐

