1. 概述
Kotlin 在 Java 集合框架基础上,通过扩展函数增强了其可用性和可读性,无需依赖如 Apache Commons 或 Guava 这类第三方库。
本文将聚焦于 Kotlin 中的排序操作,并使用 kotlin.comparisons 包实现复杂的排序规则。
2. 集合排序
Kotlin 提供了多个实用函数来简化集合排序流程。下面我们逐一介绍。
2.1. sort()
最简单的排序方式是调用 sort()
方法。 它默认使用元素的自然顺序进行升序排序:
val sortedValues = mutableListOf(1, 2, 7, 6, 5, 6)
sortedValues.sort()
log.info("$sortedValues")
输出结果为:
[1, 2, 5, 6, 6, 7]
⚠️ 注意:我们使用的是可变集合(mutableListOf
),因为 sort()
是原地排序(in-place),会直接修改原集合。
如果需要降序排序,可以使用 sortDescending()
或者 reverse()
。
2.2. sortBy()
当我们需要根据对象的某个属性排序时,可以使用 sortBy()
。 它接受一个选择器函数作为参数,该函数返回用于排序的值:
val sortedValues = mutableListOf(1 to "a", 2 to "b", 7 to "c", 6 to "d", 5 to "c", 6 to "e")
sortedValues.sortBy { it.second }
log.info("$sortedValues")
输出结果如下:
[(1, a), (2, b), (7, c), (5, c), (6, d), (6, e)]
⚠️ 同样,sortBy()
也是原地排序,所以必须使用可变集合。
如需降序排序,可使用 sortByDescending()
或 reverse()
。
2.3. sortWith()
当需要实现多条件排序时,可以使用 sortWith()
。 它接受一个 Comparator
对象作为参数。我们将在下一节介绍如何创建它:
val sortedValues = mutableListOf(1 to "a", 2 to "b", 7 to "c", 6 to "d", 5 to "c", 6 to "e")
sortedValues.sortWith(compareBy({it.second}, {it.first}))
log.info("$sortedValues")
输出结果如下(先按字母排序,再按数字排序):
[(1, a), (2, b), (5, c), (7, c), (6, d), (6, e)]
⚠️ sortWith()
也是原地排序,因此必须使用可变集合。降序排序可以使用 reverse()
,也可以自定义比较器。
3. 比较器(Comparator)
Kotlin 提供了 kotlin.comparisons 包来帮助我们构建 Comparator
,包括:
- 创建比较器
- 处理 null 值
- 反转排序
- 扩展比较规则
3.1. 比较器创建
Kotlin 提供了多种工厂函数来创建比较器以提高可读性。
最简单的比较器是 naturalOrder()
,它默认升序排序:
val ascComparator = naturalOrder<Long>()
对于包含多个属性的对象,可以使用 compareBy()
,它接受多个排序规则:
val complexComparator = compareBy<Pair<Int, String?>>({it.first}, {it.second})
上面的例子中,先按 first
排序,若相同则按 second
排序。
3.2. 处理 null 值
为了处理 null 值,可以使用 nullsFirst()
或 nullsLast()
函数。 它们分别将 null 值排在最前或最后:
val sortedValues = mutableListOf(1 to "a", 2 to null, 7 to "c", 6 to "d", 5 to "c", 6 to "e")
sortedValues.sortWith(nullsLast(compareBy { it.second }))
log.info("$sortedValues")
输出结果如下:
[(1, a), (7, c), (5, c), (6, d), (6, e), (2, null)]
可以看到,null 值被排在了最后。
3.3. 反转排序
可以通过 reverseOrder()
或 reversed()
来反转排序。 前者返回一个默认的降序比较器,后者可以作用于任意比较器对象:
reverseOrder()
3.4. 扩展比较规则
可以使用 thenBy()
或 thenByDescending()
等函数扩展比较器的排序规则。 只有当前比较器判断相等时,才会使用后续规则继续排序。
例如,先按年龄升序,再按姓名降序:
val students = mutableListOf(21 to "Helen", 21 to "Tom", 20 to "Jim")
val ageComparator = compareBy<Pair<Int, String?>> {it.first}
val ageAndNameComparator = ageComparator.thenByDescending {it.second}
students.sortWith(ageAndNameComparator)
log.info("$students")
输出结果如下:
[(20, Jim), (21, Tom), (21, Helen)]
4. sort*()
与 sorted*()
的区别
我们前面提到的 sort()
, sortBy()
, sortWith()
都是原地排序,而 sorted()
, sortedBy()
, sortedWith()
则是返回新列表。
✅ sort*()
的特点:
- 是
MutableList
的扩展函数 - 只适用于可变集合
- 返回
Unit
,即直接修改原集合(原地排序)
public actual fun <T : Comparable<T>> MutableList<T>.sort(): Unit {...}
✅ sorted*()
的特点:
- 是
Iterable
的扩展函数 - 适用于所有集合类型(包括不可变集合)
- 返回一个新的
List
,不修改原集合
public fun <T : Comparable<T>> Iterable<T>.sorted(): List<T> {...}
总结
- 如果是只读集合,只能使用
sorted*()
。 - 如果是可变集合,可以选择:
- 使用
sort*()
原地修改集合 - 使用
sorted*()
创建新集合,保留原集合不变
- 使用
5. 总结
本文介绍了 Kotlin 中常用的排序方法:
sort()
,sortBy()
,sortWith()
:适用于可变集合,原地排序sorted()
,sortedBy()
,sortedWith()
:适用于任意集合,返回新列表
我们还使用 kotlin.comparisons 包创建了复杂的比较器,支持多条件排序、null 值处理和排序规则扩展。
所有示例代码和完整项目可以参考 GitHub 仓库。