網站首頁 編程語言 正文
前言
在Android開發中,集合是我們必備的容器,Kotlin的標準庫中提供了很多處理集合的方法,而且還提供了兩種基于容器的工作方式:Collection 和 Sequence。
Collection 是我們常見的,而 Sequence 確是我們少見的,甚至很多人都沒有聽說過(是的,說的就是我 ? ?)
本篇文章就來介紹一下常用的一些集合操作符,以及 Collection 與 Sequence 的區別,到底怎么用!
Collection 的常見操作
Collection 集合,Kotlin的集合類型和Java不一樣,Kotlin的集合分為可變(讀寫)和不可變(只讀)類型(lists, sets, maps, etc),可變類型是在不可變類型前面加Mutable,以我們常用的三種集合類型為例:
List<out E> - MutableList<E>
Set<out E> - MutableSet<E>
Map<K, out V> - MutableMap<K, V>
其實他們的區別就是List實現了Collection接口,而MutableList實現的是List和MutableCollection接口。而 MutableCollection 接口實現了Collection 接口,并且在里面添加了add和remove等操作方法。
可變不可變只是為了區分只讀和讀寫的操作,他們的操作符方式都是相同的。
集合的操作符說起來可就太多了
累計
//對所有元素求和
list.sum()
//將集合中的每一個元素代入lambda表達式,然后對lambda表達式的返回值求和
list.sumBy {
it % 2
}
//在一個初始值的基礎上,從第一項到最后一項通過一個函數累計所有的元素
list.fold(100) { accumulator, element ->
accumulator + element / 2
}
//同fold,只是迭代的方向相反
list.foldRight(100) { accumulator, element ->
accumulator + element / 2
}
//同fold,只是accumulator的初始值就是集合的第一個元素,element從第二個元素開始
list.reduce { accumulator, element ->
accumulator + element / 2
}
//同reduce但方向相反:accumulator的初始值就是集合的最后一個元素,element從倒數第二個元素開始往前迭代
list.reduceRight { accumulator, element ->
accumulator + element / 2
}
val list = listOf(1, 2, 3, 4, 5, 6)
//只要集合中的任何一個元素滿足條件(使得lambda表達式返回true),any函數就返回true
list.any {
it >= 0
}
//集合中的全部元素都滿足條件(使得lambda表達式返回true),all函數才返回true
list.all {
it >= 0
}
//若集合中沒有元素滿足條件(使lambda表達式返回true),則none函數返回true
list.none {
it < 0
}
//count函數的返回值為:集合中滿足條件的元素的總數
list.count {
it >= 0
}
遍歷
//遍歷所有元素
list.forEach {
print(it)
}
//同forEach,只是可以同時拿到元素的索引
list.forEachIndexed { index, value ->
println("position $index contains a $value")
}
showFields.forEach { (key, value) ->
最大最小
//返回集合中最大的元素,集合為空(empty)則返回null
list.max()
//返回集合中使得lambda表達式返回值最大的元素,集合為空(empty)則返回null
list.maxBy { it }
//返回集合中最小的元素,集合為空(empty)則返回null
list.min()
//返回集合中使得lambda表達式返回值最小的元素,集合為空(empty)則返回null
list.minBy { it }
過濾(去除)
//返回一個新List,去除集合的前n個元素
list.drop(2)
//返回一個新List,去除集合的后n個元素
list.dropLast(2)
//返回一個新List,去除集合中滿足條件(lambda返回true)的第一個元素
list.dropWhile {
it > 3
}
//返回一個新List,去除集合中滿足條件(lambda返回true)的最后一個元素
list.dropLastWhile {
it > 3
}
//返回一個新List,包含前面的n個元素
list.take(2)
//返回一個新List,包含最后的n個元素
list.takeLast(2)
//返回一個新List,僅保留集合中滿足條件(lambda返回true)的第一個元素
list.takeWhile {
it>3
}
//返回一個新List,僅保留集合中滿足條件(lambda返回true)的最后一個元素
list.takeLastWhile {
it>3
}
//返回一個新List,僅保留集合中滿足條件(lambda返回true)的元素,其他的都去掉
list.filter {
it > 3
}
//返回一個新List,僅保留集合中不滿足條件的元素,其他的都去掉
list.filterNot {
it > 3
}
//返回一個新List,僅保留集合中的非空元素
list.filterNotNull()
//返回一個新List,僅保留指定索引處的元素
list.slice(listOf(0, 1, 2))
映射
//將集合中的每一個元素代入lambda表達式,lambda表達式必須返回一個元素
//map的返回值是所有lambda表達式的返回值所組成的新List
//例如下面的代碼和listOf(2,4,6,8,10,12)將產生相同的List
list.map {
it * 2
}
//將集合中的每一個元素代入lambda表達式,lambda表達式必須返回一個集合
//而flatMap的返回值是所有lambda表達式返回的集合中的元素所組成的新List
//例如下面的代碼和listOf(1,2,2,3,3,4,4,5,5,6,6,7)將產生相同的List
list.flatMap {
listOf(it, it + 1)
}
//和map一樣,只是lambda表達式的參數多了一個index
list.mapIndexed { index, it ->
index * it
}
//和map一樣,只不過只有lambda表達式的非空返回值才會被包含在新List中
list.mapNotNull {
it * 2
}
//根據lambda表達式對集合元素進行分組,返回一個Map
//lambda表達式的返回值就是map中元素的key
//例如下面的代碼和mapOf("even" to listOf(2,4,6),"odd" to listOf(1,3,5))將產生相同的map
list.groupBy {
if (it % 2 == 0) "even" else "odd"
}
元素
list.contains(2)
list.elementAt(0)
//返回指定索引處的元素,若索引越界,則返回null
list.elementAtOrNull(10)
//返回指定索引處的元素,若索引越界,則返回lambda表達式的返回值
list.elementAtOrElse(10) { index ->
index * 2
}
//返回list的第一個元素
list.first()
//返回list中滿足條件的第一個元素
list.first {
it > 1
}
//返回list的第一個元素,list為empty則返回null
list.firstOrNull()
//返回list中滿足條件的第一個元素,沒有滿足條件的則返回null
list.firstOrNull {
it > 1
}
list.last()
list.last { it > 1 }
list.lastOrNull()
list.lastOrNull { it > 1 }
//返回元素2第一次出現在list中的索引,若不存在則返回-1
list.indexOf(2)
//返回元素2最后一次出現在list中的索引,若不存在則返回-1
list.lastIndexOf(2)
//返回滿足條件的第一個元素的索引
list.indexOfFirst {
it > 2
}
//返回滿足條件的最后一個元素的索引
list.indexOfLast {
it > 2
}
//返回滿足條件的唯一元素,如果沒有滿足條件的元素或滿足條件的元素多于一個,則拋出異常
list.single {
it == 5
}
//返回滿足條件的唯一元素,如果沒有滿足條件的元素或滿足條件的元素多于一個,則返回null
list.singleOrNull {
it == 5
}
排序&逆序
val list = listOf(1, 2, 3, 4, 5, 6)
//返回一個顛倒元素順序的新集合
list.reversed()
/**
* 返回一個升序排序后的新集合
*/
list.sorted()
//將每個元素代入lambda表達式,根據lambda表達式返回值的大小來對集合進行排序
list.sortedBy {
it*2
}
/**
* 功能和上面一樣 -> 上面是從小到大排列,這個返回的是從大到小
*/
list.sortedDescending()
list.sortedByDescending {
it*2
}
/**
* 根據多個條件排序
* 先根據age 升序排列,若age相同,根據name升序排列,但是都是默認的升序排列
*/
personList.sortWith(compareBy({ it.age }, { it.name }))
/**
* 根據多個條件排序,自定義的規則
* 構造一個Comparator對象,完成排序邏輯:先按age降序排列,若age相同,則按name升序排列
*/
val c1: Comparator<Person> = Comparator { o1, o2 ->
if (o2.age == o1.age) {
o1.name.compareTo(o2.name)
} else {
o2.age - o1.age
}
}
personList.sortWith(c1)
//上面的自定義方式可以通過JavaBean實現Comparable 接口實現自定義的排序
data class Person(var name: String, var age: Int) : Comparable<Person> {
override fun compareTo(other: Person): Int {
if (this.age == other.age) {
return this.name.compareTo(other.name)
} else {
return other.age - this.age
}
}
}
//sorted 方法返回排序好的list(已有有排序規則的用sorted,不要用sortedby了)
val sorted = personList.sorted()
Sequence 的常見操作
Sequence 是 Kotlin 中一個新的概念,用來表示一個延遲計算的集合。Sequence 只存儲操作過程,并不處理任何元素,直到遇到終端操作符才開始處理元素,我們也可以通過 asSequence 擴展函數,將現有的集合轉換為 Sequence ,代碼如下所示
val list = mutableListOf<Person>()
for (i in 1..10000) {
list.add(Person("name$i", (0..100).random()))
}
list.asSequence()
當我們拿到結果之后我們還能通過toList再轉換為集合。
list.asSequence().toList()
Sequence的操作符絕大部分都是和 Collection 類似的。常用的一些操作符是可以直接平替使用的。
val list2 = list.asSequence()
.filter {
it.age > 50
}.map {
it.name
}.take(3).toList()
居然他們的操作符都長的一樣,效果也都一樣,導致 Sequence 與 Collection 就很類似,那么既生瑜何生亮!為什么需要這么個東西?既然 Collection 能實現效果為什么還需要 Sequence 呢?他們的區別又是什么呢?
區別與對比
Collection 是立即執行的,每一次中間操作都會立即執行,并且把執行的結果存儲到一個容器中,沒多一個中間操作符就多一個容器存儲結果。
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
比如常用的 map 和 filter 都是會新建一個 ArrayList 去存儲結果,
Sequence 是延遲執行的,它有兩種類型,中間操作和末端操作 ,主要的區別是中間操作不會立即執行,它們只是被存儲起來,中間操作符會返回另一個Sequence,僅當末端操作被調用時,才會按照順序在每個元素上執行中間操作,然后執行末端操作。
public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R> {
return TransformingSequence(this, transform)
}
public fun <T> Sequence<T>.filter(predicate: (T) -> Boolean): Sequence<T> {
return FilteringSequence(this, true, predicate)
}
比如常用的 map 和 filter 都是直接返回 Sequence 的this 對象。
public inline fun <T> Sequence<T>.first(predicate: (T) -> Boolean): T {
for (element in this) if (predicate(element)) return element
throw NoSuchElementException("Sequence contains no element matching the predicate.")
}
然后在末端操作中,會對 Sequence 中的元素進行遍歷,直到預置條件匹配為止。
這里我們舉一個示例來演示一下:
我們使用同樣的篩選與轉換,來看看效果
val list = mutableListOf<Person>()
for (i in 1..10000) {
list.add(Person("name$i", (0..100).random()))
}
val time = measureTimeMillis {
val list1 = list.filter {
it.age > 50
}.map {
it.name
}.take(3)
YYLogUtils.w("list1$list1")
}
YYLogUtils.w("耗費的時間$time")
val time2 = measureTimeMillis {
val list2 = list.asSequence()
.filter {
it.age > 50
}.map {
it.name
}.take(3).toList()
YYLogUtils.w("list2$list2")
}
YYLogUtils.w("耗費的時間2$time2")
運行結果:
當集合數量為10000的時候,執行時間能優秀百分之50左右:
當集合數量為5000的時候,執行時間相差比較接近:
當集合數量為3000的時候,此時的結果就反過來了,Sequence延時執行的優化效果就不如List轉換Sequence再轉換List了:
總結
Collection 會立即執行對數據的操作,而 Sequence 則是延遲執行,如果數據量比較小,可以使用 Collection ,如果處理的數據量比較大,Sequence 是最好的選擇,因為不會創建中間結果集,內存開銷更小。
根據上面的測試發現,也不能無腦的使用 Sequence 來優化篩選轉換效果,當數據量沒有達到一定程度的時候,可能會出現負優化的效果。
當然我們的對象是很簡單的對象,我們的篩選也是簡單篩選,當對象復雜度到一定程度,篩選條件復雜到一定程度,集合的數量到一定的程度,我們才可以考慮 使用 Sequence 來達到優化效果。
Sequence 的具體的使用還是需要具體場景具體分析來使用,就我們Android應用開發的場景來說,很少會出現這么大的數據量,所以一般來說我們平常開發使用 Collection 即可。
好了,本文的全部代碼與Demo都已經開源。有興趣可以看這里。項目會持續更新,大家可以關注一下。
原文鏈接:https://juejin.cn/post/7155765126627344392
相關推薦
- 2022-06-02 一篇文章帶你了解Python中的裝飾器_python
- 2022-12-11 Go?map發生內存泄漏解決方法_Golang
- 2022-03-22 nginx開啟gzip壓縮的完整步驟記錄_nginx
- 2022-11-27 搭建?Selenium+Python開發環境詳細步驟_python
- 2022-04-23 css3新特性transition初體驗
- 2022-10-11 C++函數對象Functor與匿名函數對象Lambda表達式詳解_C 語言
- 2022-11-17 React通過classnames庫添加類的方法_React
- 2022-09-15 git驗證線上的版本是否符合預期_相關技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支