大部分業(yè)務(wù)代碼,都在處理數(shù)據(jù)!所以,高效很重要?。?/h1>
一、序
在日常編寫業(yè)務(wù)代碼的時候,大部分代碼其實就是在處理數(shù)據(jù)、展示數(shù)據(jù),這些操作占用了我們大量的編碼時間。
比較常見的,從某個集合中找到滿足某個限制條件的數(shù)據(jù)。最簡單的方法就是利用循環(huán)來遍歷數(shù)據(jù)集合,找到我們需要的數(shù)據(jù)。
- fun findFive() {
- val list = arrayListOf(1, 2, 3, 4, 5, 6, 7)
- val findList = mutableListOf<Int>()
- for (i in list) {
- if (i > 5) {
- findList.add(i)
- }
- }
- // findList
- }
大部分數(shù)據(jù)集合的操作,我們都可以通過循環(huán)來解決,針對一些更復(fù)雜的操作,例如從兩個集合中找出交集集合,可以通過兩層嵌套循環(huán)解決。
除了用粗暴的循環(huán)解決問題之外,我們還可以利用 Kotlin 對集合封裝的一些高階函數(shù),讓我們對集合的處理,更得心應(yīng)手。
上面例子中的需求,用 Kotlin 集合的高階函數(shù),一個方法就可以解決。
- val findList = list.filter { it > 5 }
集合的高階函數(shù),還有很多更巧妙的使用方式。
本文就從業(yè)務(wù)場景出發(fā),講解 Kotlin 中與集合相關(guān)的一些高階函數(shù)的使用和場景。
二、集合的高階函數(shù)
Kotlin 原生就支持 Lambda 表達式,高階函數(shù)中都可以基于 Lambda 來實現(xiàn)的。
假如我們需要實現(xiàn)一個公司內(nèi)部的員工管理系統(tǒng),那我們首先定義一個員工類(EmployeModel)。
- data class EmployeModel(var id: Long, // 員工 ID
- var name: String,// 姓名
- var age: Int,// 年齡
- var department: String,// 部門
- var salary: Int // 薪水
- ) {
- }
- // var list = arrayListOf(EmployeModel(...),...)
接下來我們就圍繞這個員工類,實現(xiàn)各種必要的需求。
2.1 數(shù)據(jù)篩選
需求:從給定的員工列表中,篩選出月薪大于 2w 的所有員工。
正如前文中舉例的樣子,對于數(shù)據(jù)篩選,可以使用 .filter() 方法,此方法接受一個 Boolean 類型的條件,它會返回所有滿足此條件的數(shù)據(jù)集合,最終其實返回的依然是一個 List。
- var findList = list.filter {it.salary > 20000}
.filter() 方法內(nèi)不是其實也是用 for 循環(huán)來實現(xiàn)的,沒有什么高深的。而除了filter() 之外,Kotlin 還提供了帶上一些特殊條件的過濾器。
例如:
- filterIndexed,帶 Index 的過濾器。
- filterNot,過濾所有不滿足條件的數(shù)據(jù)。
這里就不展開了。
2.2 數(shù)據(jù)排序
需求:將給定的員工列表,根據(jù)員工的薪酬進行排序。
通過 sortedBy() 方法,指定排序依據(jù),它會根據(jù)指定的數(shù)值,進行自然順序排序。
- var findList = list.sortedBy{it.salary}
sortedBy() 方法為順序排序,如果想要逆序排序,可以使用 sortedByDescending()方法。
需要注意的是,除了 sortedBy() 之外,配套的還有 sortBy() 和sortByDescending() 方法,這兩套方法的區(qū)別在于操作的集合以及返回值是什么。
在 Kotlin 中,所有的集合可以分為可變集合和不可變集合,sortBy/sortByDescending 方法,只能作用在可變集合上,它返回的是一個 Unit 類型的對象,會在集合上進行自排序。而 sortBy() 方法會返回一個排序好的新集合。
2.3 集合切片
需求:將給定的員工列表,只顯示前 100 位員工。
如果想獲取某個集合中前 N 個員工,可以使用 take() 方法。
- var findList = list.take(100)
和 take() 取前 N 個員工類似,還可以使用 takeLast() 方法獲取末尾 N 個員工。
更靈活的切片,例如取第 10~20 的員工,就需要用到 slice() 方法,如果想要截取連續(xù)的一段數(shù)據(jù),可以使用 IntRange 類來指定。
- var findList = list.slice(IntRange(10,20))
slice() 方法還支持直接指定集合的下標,來獲取不連續(xù)的數(shù)據(jù),例如選取排名第 1、10、25 位員工。
- var findList = list.slice(arrayListOf(1, 10, 15))
2.4 集合變換
需求:要調(diào)薪了,準備將一批員工漲薪 20%。那就需要批量上報一批員工信息,和服務(wù)端商量,只需要上報這一批調(diào)薪的員工 ID 即可。
原本我們有一個員工類的集合 List
這里就可以用到 map() 方法,它可以對集合中的數(shù)據(jù)做一次變換。
- var findList = list.map { it.id }
map() 原本是不帶 index 的,如果對 index 有需求,可以使用 mapIndexed()方法。
map() 代表了一種一對一的變換關(guān)系。如果想做到一對多,可以使用 flatMap()方法,flatMap() 可以將每個元素變換為一個新的集合,再將其平鋪成一個集合。
2.5 數(shù)據(jù)分組
需求:將所有的員工,用年齡以 20 歲為界限進行分組。
這種兩分的分組,可以使用 partition() 方法,它需要制定一個分組的條件,以 Boolean 類型區(qū)分,最終返回一個 Pair 對象,Pair.first 就是滿足條件的集合,Pair.second 則是不滿足條件的集合。
- var findList = list.partition { it.age > 20 }
- findList.first.forEach{
- Log.i("cxmyDev","${it.name} 大于 20");
- }
- findList.second.forEach{
- Log.i("cxmyDev","${it.name} 小于或等于 20");
- }
此時需求又變了,要根據(jù)部門來分組,公司的部門可不止兩個。針對這種情況,可以通過 groupBy() 方法進行分組。
- var findList = list.groupBy { it.department }
groupBy() 方法會返回一個 Map
2.6 其他操作
前面介紹的這些針對 Kotlin 集合的高階函數(shù),基本上滿足我對集合的大部分操作。當(dāng)然,Kotlin 還有一些更豐富的高階函數(shù),有興趣可以直接查看 _Collections.kt 源碼。
高階函數(shù)之所以強大,更多的是因為支持鏈式調(diào)用,我們可以通過一個很清晰代碼結(jié)構(gòu),就實現(xiàn)我們對集合的復(fù)雜需求。
我拍腦袋舉個例子,取員工集合里,A 部門的員工,根據(jù)員工 ID 排序后,取前 100 號員工的 ID 進行上報。
- var findList = list.filter {
- "A".equals(it.department)
- }.sortedBy {
- it.id
- }.map {
- it.id
- }.take(100)
這樣一個復(fù)雜的集合操作,在 Kotlin 的高階函數(shù)中,只需要一組鏈式調(diào)用即可實現(xiàn)。
另外需要注意的是,高階函數(shù)中很多方法,都不是線程安全的,所以在使用的時候,需要考慮多線程場景下的同步問題。
三、小結(jié)時刻
本文介紹了一些比較常用的集合操作函數(shù),隨著 Kotlin 版本的升級,還有會更多的高階函數(shù)添加進來,更多細節(jié)可以閱讀源碼或者參考 kotlin.collections 文檔。
【本文為51CTO專欄作者“張旸”的原創(chuàng)稿件,轉(zhuǎn)載請通過微信公眾號聯(lián)系作者獲取授權(quán)】