排序
元素順序是某些集合類型的重要面向。 例如,如果兩個包含相同元素的列表其元素順序不同,則這兩個列表是不相等的。
在 Kotlin 中,可以透過幾種方式定義物件的順序。
第一種是 自然順序 (natural order)。它是為 Comparable 介面的實作者所定義的。當沒有指定其他順序時,自然順序會用於對它們進行排序。
大多數內建型別都是可比較的:
- 數值型別使用傳統的數值順序:
1大於0;-3.4f大於-5f,依此類推。 Char和String使用 字典序 (lexicographical order):b大於a;world大於hello。
要為使用者定義型別定義自然順序,請讓該型別實作 Comparable。 這需要實作 compareTo() 函式。compareTo() 必須接收另一個相同型別的物件作為引數,並傳回一個整數值來顯示哪個物件較大:
- 正值表示接收者物件較大。
- 負值表示它小於引數。
- 零表示物件相等。
以下是一個用於對版本進行排序的類別,版本由主版本 (major) 和次版本 (minor) 部分組成。
class Version(val major: Int, val minor: Int): Comparable<Version> {
override fun compareTo(other: Version): Int = when {
this.major != other.major -> this.major compareTo other.major // 中綴形式的 compareTo()
this.minor != other.minor -> this.minor compareTo other.minor
else -> 0
}
}
fun main() {
println(Version(1, 2) > Version(1, 3))
println(Version(2, 0) > Version(1, 5))
}自訂順序 (Custom orders) 讓你可以依照自己喜歡的方式對任何型別的執行個體進行排序。 特別是,你可以為不可比較的物件定義順序,或者為可比較型別定義自然順序以外的順序。 要為某個型別定義自訂順序,請為其建立一個 Comparator。 Comparator 包含 compare() 函式:它接收一個類別的兩個執行個體,並傳回兩者之間比較的整數結果。 該結果的解讀方式與上述 compareTo() 的結果相同。
fun main() {
val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
println(listOf("aaa", "bb", "c").sortedWith(lengthComparator))
}有了 lengthComparator,你就能夠依據字串的長度而不是預設的字典序來排列字串。
定義 Comparator 的一種更簡短方式是使用標準函式庫中的 compareBy() 函式。compareBy() 接收一個 Lambda 函式,該函式從執行個體產生一個 Comparable 值,並將自訂順序定義為所產生值的自然順序。
使用 compareBy(),上述範例中的長度比較器如下所示:
fun main() {
println(listOf("aaa", "bb", "c").sortedWith(compareBy { it.length }))
}你也可以根據多個標準定義順序。 例如,要按長度排序字串,並在長度相等時按字母順序排序,你可以寫成:
fun main() {
val sortedStrings = listOf("aaa", "bb", "c", "b", "a", "aa", "ccc")
.sortedWith { a, b ->
when (val compareLengths = a.length.compareTo(b.length)) {
0 -> a.compareTo(b)
else -> compareLengths
}
}
println(sortedStrings)
// [a, b, c, aa, bb, aaa, ccc]
}由於按多個標準排序是常見的情境,Kotlin 標準函式庫提供了 thenBy() 函式,你可以用它來增加次要排序規則。
例如,你可以將 compareBy() 與 thenBy() 結合使用,先按長度排序字串,再按字母順序排序,就像前面的範例一樣:
fun main() {
val sortedStrings = listOf("aaa", "bb", "c", "b", "a", "aa", "ccc")
.sortedWith(compareBy<String> { it.length }.thenBy { it })
println(sortedStrings)
// [a, b, c, aa, bb, aaa, ccc]
}Kotlin 集合套件提供了以自然、自訂甚至隨機順序對集合進行排序的函式。 在此頁面中,我們將介紹適用於 唯讀 集合的排序函式。 這些函式將其結果作為一個新集合傳回,該集合包含原集合中按要求順序排列的元素。 若要了解如何對 可變 集合進行就地 (in place) 排序的函式,請參閱 List 專屬操作。
自然順序
基本函式 sorted() 和 sortedDescending() 傳回集合元素,並根據其自然順序按升序和降序排列。 這些函式適用於 Comparable 元素的集合。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println("Sorted ascending: ${numbers.sorted()}")
println("Sorted descending: ${numbers.sortedDescending()}")
}自訂順序
對於以自訂順序排序或對不可比較物件進行排序,可以使用函式 sortedBy() 和 sortedByDescending()。 它們接收一個選擇器函式,該函式將集合元素對應到 Comparable 值,並按這些值的自然順序對集合進行排序。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedBy { it.length }
println("Sorted by length ascending: $sortedNumbers")
val sortedByLast = numbers.sortedByDescending { it.last() }
println("Sorted by the last letter descending: $sortedByLast")
}要為集合排序定義自訂順序,你可以提供自己的 Comparator。 為此,請呼叫 sortedWith() 函式並傳入你的 Comparator。 使用此函式,按長度排序字串如下所示:
fun main() {
val numbers = listOf("one", "two", "three", "four")
println("Sorted by length ascending: ${numbers.sortedWith(compareBy { it.length })}")
}反轉順序
你可以使用 reversed() 函式以反轉的順序檢索集合。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.reversed())
}reversed() 傳回一個包含元素副本的新集合。 因此,如果你稍後更改原始集合,這不會影響先前獲得的 reversed() 結果。
另一個反轉函式 asReversed() 傳回相同集合執行個體的反轉檢視,因此如果原始列表不打算更改,它可能比 reversed() 更輕量且更值得推薦。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)
}如果原始列表是可變的,其所有變更都會反映在其反轉檢視中,反之亦然。
fun main() {
val numbers = mutableListOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed()
println(reversedNumbers)
numbers.add("five")
println(reversedNumbers)
}然而,如果列表的可變性未知,或者來源根本不是列表,則 reversed() 更值得推薦,因為其結果是一個副本,未來不會改變。
隨機順序
最後,有一個函式可以傳回包含隨機順序集合元素的新 List —— shuffled()。 你可以不帶引數呼叫它,也可以傳入一個 Random 物件。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.shuffled())
}