在處理資料時,順序有時是重要的。比方說,我們想要將 Collection 裡的數字從小到大來排,或是想將 Collection 裡的名字從 a 到 z 排。在比較兩個 List 的時候,即便內容元素都相同,但順序不同也會被視為不同的 List,由此可見順序在資料處理的重要性。在這個章節裡,我們就要來看一下 Collection 可以有哪些排序 method 可以用?
Kotlin 排序的功能很單純,要依照序列正排就用 sorted()
、要反排就用 sortedDescending()
,Kotlin 會自動依照自然排序(Natural Order)排列所有元素。所謂自然排序對數字(Numeric)類型來說就是照著數學觀念的大小來做排序,1 大於 0、-3 大於 -5;而對字元(Char)或字串(String)的話,則依 a-z 的順序來排,非常直覺。
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sorted() // [four, one, three, two]
val sortedDescNumbers = numbers.sortedDescending() // [two, three, one, four]
假如今天需要客製化排序的邏輯,或是放在 Collection 裡的元素沒有實作 Comparable
介面的話,那就需要用 sortedBy()
或 sortedByDescending()
搭配 Lambda 來使用了。
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedBy { it.length } // [one, two, four, three]
val sortedByLast = numbers.sortedByDescending { it.last() } // [four, two, one, three]
另一種自定排序的方式,就是用 sortedWith()
並傳入一個 Comparator
的介面,所以上面的例子可以改寫成:
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numbers.sortedWith(compareBy { it.length }) // [one, two, four, three]
排序的重要之處就是在拿取元素的時候可以照順序拿,但有時需要依照原本的順序反著拿,這時最快的方式就是將排序反轉。在 Kotlin 你也不需要自己用迴圈來處理,只需要呼叫 reversed()
即可。
val numbers = listOf("one", "two", "three", "four")
val reversedNumbers = numbers.reversed() //
基本上,所有排序的 method 都不會更動到原本的 Collection。也就是說,今天若 numbers
的內容更動了,reversedNumbers
並不會依照新的內容重新排序,這樣會有點不方便。若你希望可以產生出一個類似 View 的概念,Kotlin 也提供一個 asReversed()
的 method,它會自動同步每次的更新。
val numbers = mutableListOf("one", "two", "three", "four")
val reversedNumbers = numbers.asReversed() // 這時 reversedNumbers 是 [four, three, two, one]
numbers.add("five") // 這時 reversedNumbers 變成 [five, four, three, two, one]
跟一般的排序目的不同,有時為了創造隨機性,需要把 Collection 裡的排序故意弄亂。比方說,今天你要寫一個尾牙抽獎程式,Collection 就是抽獎箱,裡面裝著抽獎名單,每次抽獎前都要先把抽獎箱搖一搖以示公平,這時你就會需要隨機排序。在 Kotlin 裡要讓 Collection 裡的元素隨機排序很簡單,只要使用 shuffled()
就可以了。要注意的是,shuffled()
會回傳整個隨機排序過後的 Collection,跟前面章節裡隨機取出一個元素的 random()
在行為上是不一樣的喔!
val numbers = listOf("one", "two", "three", "four")
val shuffledNumbers = numbers.shuffled() // 每次出現的結果都會不同
在這個章節裡,我們討論了幾個常用的排序方式。要提醒一下,這些排序方式大多數都不會影響到原本的 Collection,所以要保留結果的話,就要宣告一個新的變數來儲存,在資料操作時要特別注意這個特點。
為了一覽這些 API 在不同 Collection 上的行為,以下用表格來整理本章所討論到的 method:
行為 | Array | List | Set | Map | |
---|---|---|---|---|---|
sorted() | 自然排序 | v | v | v | x |
sortedDescending() | 反向自然排序 | v | v | v | x |
sortedBy{} | 自定排序 | v | v | v | x |
sortedByDescending{} | 反向自定排序 | v | v | v | x |
sortedWith() | 自定排序 | v | v | v | x |
reversed() | 反轉排序 | v | v | v | x |
asReversed() | 取得反轉排序 View | x | v | x | x |
shuffled() | 隨機排序 | x | v | v | x |