1. 简介
字典序排序(lexicographical order)本质上就是按字母顺序排列字符串,类似于词典中的单词排序方式。例如,给定字符串列表 ["banana", "Apple", "cherry", "Date"]
,排序后应为 ["Apple", "banana", "cherry", "Date"]
。✅ 注意:这个过程通常是忽略大小写的。
这在处理用户姓名、标签、配置项等需要自然语言排序的场景中非常常见。如果直接按 ASCII 排序,大写字母会优先于小写字母(比如 "A"
< "a"
),导致结果不符合预期 ❌。
本文将介绍在 Kotlin 中实现不区分大小写的字典序排序的几种方法,并分析其适用场景和踩坑点。
2. 手动实现排序逻辑(迭代法)
最原始的方式是自己实现排序算法,比如使用双重循环进行冒泡或选择排序。核心思路是:
- 遍历每个元素
words[i]
- 将其与后续所有元素
words[j]
进行比较 - 比较前先统一转为小写,避免大小写影响排序结果
⚠️ 直接使用 >
比较字符串会基于 Unicode 值,无法做到忽略大小写。
fun sortWithCustomMethod(words: Array<String>) {
for (i in 0..words.size - 2) {
for (j in i + 1 until words.size) {
if (words[i].lowercase() > words[j].lowercase()) {
val temp = words[i]
words[i] = words[j]
words[j] = temp
}
}
}
}
✅ 测试验证
@Test
fun `sort using custom method`() {
val words = arrayOf("banana", "apple", "cherry", "date", "A", "Result", "Union")
sortWithCustomMethod(words)
assertContentEquals(
arrayOf("A", "apple", "banana", "cherry", "date", "Result", "Union"),
words
)
}
📌 缺点:时间复杂度 O(n²),性能差,仅适合学习理解排序原理,生产环境不推荐 ❌。
3. 使用 forEach
+ Lambda 实现迭代
我们可以通过 Kotlin 的 forEachIndexed
和嵌套 forEach
来简化循环结构,避免手动管理索引边界。
这种方式逻辑清晰,代码更函数式一些,但本质仍是 O(n²) 的选择排序变体。
fun sortStringsIgnoreCaseForEach(words: List<String>): List<String> {
val sortedWords = words.toMutableList()
sortedWords.forEachIndexed { i, word ->
(i until sortedWords.size).forEach { j ->
if (word.lowercase().compareTo(sortedWords[j].lowercase()) > 0) {
sortedWords[i] = sortedWords[j].also { sortedWords[j] = sortedWords[i] }
}
}
}
return sortedWords
}
✅ 测试用例
@Test
fun `sort using forEachIndexed method`() {
val words = listOf("banana", "apple", "cherry", "date", "A", "Result", "Union")
val sortedWords = sortStringsIgnoreCaseForEach(words)
assertContentEquals(
listOf("A", "apple", "banana", "cherry", "date", "Result", "Union"),
sortedWords
)
}
📌 注意点:
- 使用了
compareTo()
而不是>
,更安全且语义明确 also
用于交换值,是 Kotlin 常见技巧- 返回新列表,原列表不受影响 ✅
但仍属于低效实现,仅作教学参考。
4. 使用 sortedWith()
配合自定义 Comparator
Kotlin 提供了强大的标准库函数 sortedWith()
,它接受一个 Comparator<T>
并返回一个新的已排序列表。
我们可以使用 Java 标准库提供的 String.CASE_INSENSITIVE_ORDER
,这是一个现成的不区分大小写的比较器。
@Test
fun `sort using sortedWith() method`() {
val words = arrayOf("banana", "apple", "cherry", "date", "A", "Result", "Union")
val sortedWords = words.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it })
assertContentEquals(
listOf("A", "apple", "banana", "cherry", "date", "Result", "Union"),
sortedWords
)
}
📌 关键点解析:
compareBy(Comparator)
是构建比较器的工厂函数String.CASE_INSENSITIVE_ORDER
是 Java 中定义的静态Comparator<String>
- 排序稳定,性能良好 ✅
- 返回的是新列表,原数组不变 ✅
✅ 推荐场景:需要高度定制化排序规则时使用,比如组合多个字段排序。
5. 使用 sortedBy()
配合映射函数
这是最简洁、最常用的写法。sortedBy()
接收一个 lambda,表示“根据什么值来排序”。
我们要实现忽略大小写的字典序,只需将每个字符串映射为其小写形式即可。
@Test
fun `sort using sortedBy() method`() {
val words = arrayOf("banana", "apple", "cherry", "date", "A", "Result", "Union")
val sortedWords = words.sortedBy { it.lowercase() }
assertContentEquals(
listOf("A", "apple", "banana", "cherry", "date", "Result", "Union"),
sortedWords
)
}
✅ 优势总结
- 语法极简,一目了然 ✅
- 内部使用高效排序算法(Timsort)
- 支持链式调用,可扩展性强
- 是 Kotlin 社区中最常见的做法
⚠️ 踩坑提醒
sortedBy()
返回新列表;若需原地排序,使用sortWith()
或sortBy()
(作用于 MutableList)- 对于空字符串或 null 值需额外处理(本文假设输入非空)
6. 总结
方法 | 是否推荐 | 说明 |
---|---|---|
手动迭代 | ❌ | 教学用途,性能差 |
forEach 实现 |
❌ | 函数式写法但效率低 |
sortedWith() + Comparator |
✅ | 灵活,适合复杂排序逻辑 |
sortedBy() + lowercase() |
✅✅✅ | 最佳实践,简洁高效 |
📌 最终建议:
在绝大多数场景下,优先使用
sortedBy { it.lowercase() }
实现不区分大小写的字典序排序。代码清晰、性能优秀,符合 Kotlin 的惯用法(idiomatic Kotlin)。
只有在需要复合排序(如先按长度再按名称)或复用已有 Comparator
时,才考虑 sortedWith()
。