如何在Kotlin流中使用sortedByDescending实现单流式处理?
问题背景
需求是实现球员数据的流式处理:
- 读取标准输入中包含球员队伍、姓名、参赛场次、得分的行数据
- 筛选出参赛场次≥15的球员
- 按场均得分(ppg)降序排序
- 打印排名前十的球员
已实现的Java单流方案:
// JAVA SOLUTION - WORKS new BufferedReader(new InputStreamReader(System.in)) .lines() .map(line -> line.split(",")) .map(Player::fromLine) .filter(p -> p.games >= 15) .sorted((p1, p2) -> Double.compare(p2.ppg, p1.ppg)) .limit(10) .map(Player::reportLine) .forEach(System.out::println);
(注:Player为自定义记录类,fromLine方法负责解析数据并计算场均得分ppg)
尝试直接转成Kotlin时遇到报错:
// KOTLIN ATTEMPT - THIS DOES NOT WORK BufferedReader(InputStreamReader(System.`in`)) .lines() .map { it.split(",") } .map { Player.fromLine(it) } .filter { it.games >= 15 } .sortedByDescending { it.ppg } // ERROR ON THIS LINE .take(10) .map { it.reportLine() } .forEach { println(it) }
报错信息:
error: unresolved reference. None of the following candidates is applicable because of receiver type mismatch: ...
目前通过转成List分两步实现了功能:
// KOTLIN SOLUTION - WORKS (but can it done in one shot?) val players = BufferedReader(InputStreamReader(System.`in`)) .lines() .map { it.split(",") } .map { Player.fromLine(it) } .filter { it.games >= 15 } .toList() players.sortedByDescending { it.ppg } .take(10) .map { it.reportLine() } .forEach { println(it) }
疑问
- 能否将排序步骤整合进单流处理中?
- 为何非工作方案中filter的返回结果不是合法的可迭代对象?
解答
1. 可以整合进单流处理,两种实现方式
方式一:使用Java Stream的sorted方法传入Comparator
因为BufferedReader.lines()返回的是Java Stream,可以直接用Java Stream自带的sorted方法,传入Kotlin lambda作为Comparator:
BufferedReader(InputStreamReader(System.`in`)) .lines() .map { it.split(",") } .map { Player.fromLine(it) } .filter { it.games >= 15 } .sorted { p1, p2 -> p2.ppg.compareTo(p1.ppg) } .limit(10) .map { it.reportLine() } .forEach { println(it) }
也可以用Comparator的链式写法:
BufferedReader(InputStreamReader(System.`in`)) .lines() .map { it.split(",") } .map { Player.fromLine(it) } .filter { it.games >= 15 } .sorted(Comparator.comparingDouble<Player> { it.ppg }.reversed()) .limit(10) .map { it.reportLine() } .forEach { println(it) }
方式二:将Java Stream转为Kotlin Sequence
通过asSequence()把Java Stream转换成Kotlin的Sequence,这样就能直接使用Kotlin的sortedByDescending扩展函数:
BufferedReader(InputStreamReader(System.`in`)) .lines() .asSequence() // 转换为Kotlin Sequence .map { it.split(",") } .map { Player.fromLine(it) } .filter { it.games >= 15 } .sortedByDescending { it.ppg } .take(10) .map { it.reportLine() } .forEach { println(it) }
2. 关于filter返回结果的类型问题
BufferedReader.lines()返回的是java.util.stream.Stream(Java标准库的流类型),它的filter方法返回的依然是Java Stream,而不是Kotlin的Iterable或Sequence类型。
Kotlin的sortedByDescending是Kotlin标准库专门为Iterable、Sequence等Kotlin原生集合/序列类型提供的扩展函数,Java Stream并不属于这些类型体系,因此无法直接调用该方法,这就是报错的核心原因。
当你调用toList()后,得到的是Kotlin的List(属于Iterable接口),自然就能使用sortedByDescending这类扩展函数了。
内容的提问来源于stack exchange,提问作者Ray Toal

