天天看點

Kotlin 學習筆記(十二)kotlin中 集合序列使用、操作符彙總、list和sequence比較

前言

本篇部落格為本人學習Kotlin 中所遇到的問題,如果哪裡寫的不對,希望歐大佬幫忙指出,多謝。

1. 集合的基礎使用

kotlin 中的集合主要為:Array、List、Set、Map , Sequence

1.1 數組

val arr = arrayOf("1",2,3,4)
           

1.2 List

不可變集合 ( listOf() )

val arr = arrayOf("1","2",3,4,5)   
val list1 = listOf(1,2,"3",4,"5")                // 随意建立         
val list2 = listOf<String>("1","2","3","4","5")  // 确定元素的值類型
val list3 = listOf(arr)                          // 可傳入一個數組

以下代碼是錯誤的。因為List<E>隻能是不可變集合。而add、remove、clear等函數時MutableList中的函數
list1.add() 
list1.remove
           

可變集合 ( mutableListOf )

val arr = arrayOf("1","2",3,4)
val mutableList1 = mutableListOf(1,2,3,4,"5")                // 随意建立         
val mutableList2 = mutableListOf<String>("1","2","3","4","5")  // 确定元素的值類型
val mutableList3 = mutableListOf(arr)                          // 可傳入一個數組
val mutableList : ArrayList<String>  // 這裡的ArrayList<>和Java裡面的ArrayList一緻

mutableList1.add("6")  // 添加元素
mutableList1.add("7")
mutableList1.remove(1)   // 删除某一進制素
           

1.3 Set

val set1 = setOf(1,2,"3","4","2",1,2,3,4,5)
val mutableSet1 = mutableSetOf(1,2,"3","4","2",1,2,3,4,5)

// 這裡的HashSet<>和Java裡面的HashSet<>一緻
val mutableSet2 : HashSet<String>  

// 輸出結果 : 1 2 3 4 2 3 4 5 
           

  當我們看到輸出結果的時候 發現所有的重複 資料都沒有了,這裡的特點也是kotlin和java 保持了一緻。

1.4 Map

// 以鍵值對的形式出現,鍵與值之間使用to

val map1 = mapOf("key1" to 2 , "key2" to 3)
val map2 = mapOf<Int,String>(1 to "value1" , 2 to "value2")
val mutableMap = mutableMapOf("key1" to 2 , "key1" to 3)

// 同Java中的HashMap
val hashMap = hashMapOf("key1" to 2 , "key1" to 3)   

val map = val map1 = mapOf("key1" to 2 , "key1" to 3 ,
							 "key1" to "value1" , "key2" to "value2")

//輸出結果 key1 	 value1   , key2 	 value2

           

當我們的鍵存在重複時,集合會過濾掉之前重複的元素。value 隻保留了最後一個元素。而過濾掉了之前key值相同的所有元素。

2 List 和 Sequence 選擇使用

  相信大多Android 開發者都了解集合和序列的差別,那麼我們應該如何更好的使用這兩個?

總結List的使用場景:

  1. 量級比較小的集合元素(當數量比較少的時候 List 和 Sequence 的循環耗時相差無幾)
  2. 通路索引元素 (Lists(集合) 是有索引的,Sequences(序列) 則必須逐項進行.其實和 java 中的 ArrayList 和 LinkedList 知識點差不多)
  3. 傳回/傳遞給其他的函數 (每次疊代Sequences(序列) 時,都會計算元素。Lists(集合) 中的元素隻計算一次,然後存儲在記憶體中。)

我們這裡之舉一個例子通過耗時的循環來看下兩者差別

Kotlin

list.map { it *3 }
  .filter { it % 2 == 0 }
  .count { it < 10 }

           

Kotlin code

Object element$iv$iv;
      int it;
      while(var5.hasNext()) {
         element$iv$iv = var5.next();
         it = ((Number)element$iv$iv).intValue();
         Integer var12 = it * 3;
         destination$iv$iv.add(var12);
      }

      $receiver$iv = (Iterable)((List)destination$iv$iv);
      destination$iv$iv = (Collection)(new ArrayList());
      var5 = $receiver$iv.iterator();

      while(var5.hasNext()) {
         element$iv$iv = var5.next();
         it = ((Number)element$iv$iv).intValue();
         if (it % 2 == 0) {
            destination$iv$iv.add(element$iv$iv);
         }
      }

      $receiver$iv = (Iterable)((List)destination$iv$iv);
      if (!($receiver$iv instanceof Collection) || !((Collection)$receiver$iv).isEmpty()) {
         int count$iv = 0;
         Iterator var13 = $receiver$iv.iterator();

         while(var13.hasNext()) {
            Object element$iv = var13.next();
            int it = ((Number)element$iv).intValue();
            if (it < 10) {
               ++count$iv;
            }
         }
      }
           

  你會發現Kotlin編譯器會建立三個while循環.而Sequences則隻會存在一個循環,是以在循環多的情況下 還是Sequences 比較快一點。

kotlin

val result = list.asSequence()
            .map{ println("In Map"); it * 2 }
            .filter { println("In Filter");it % 3  == 0 }
    println("Before Average")
    println(result.average())
           

Kotlin code

List list = CollectionsKt.listOf(new Integer[]{1, 2, 3, 4, 5, 6});
      Sequence result = SequencesKt.filter(SequencesKt.map(CollectionsKt.asSequence((Iterable)list), (Function1)null.INSTANCE), (Function1)null.INSTANCE);
      String var3 = "Before Average";
      System.out.println(var3);
      double var5 = SequencesKt.averageOfInt(result);
      System.out.println(var5);
           

  序列(Sequences) 的秘訣在于它們是共享同一個疊代器(iterator) —序列允許 map操作 轉換一個元素後,然後立馬可以将這個元素傳遞給 filter操作 ,而不是像集合(lists) 一樣等待所有的元素都循環完成了map操作後,用一個新的集合存儲起來,然後又周遊循環從新的集合取出元素完成filter操作。

3. 操作符彙總

  下面對這6類操作符進行一一的講解,不過這裡就不對他們的源碼進行分析。大多都存在于_Collections.kt這個檔案中。下邊的測試代碼和運作結果 我都 将結果和 方法放在一起了 好對比。

3.1 元素操作符

  • contains(元素) : 檢查集合中是否包含指定的元素,若存在則傳回true,反之傳回false
  • elementAt(index) : 擷取對應下标的元素。若下标越界,會抛出IndexOutOfBoundsException(下标越界)異常,同get(index)一樣
  • elementAtOrElse(index,{…}) : 擷取對應下标的元素。若下标越界,傳回預設值,此預設值就是你傳入的下标的運算值
  • elementAtOrNull(index) : 擷取對應下标的元素。若下标越界,傳回null
  • first() : 擷取第一個元素,若集合為空集合,這會抛出NoSuchElementException異常
  • first{} : 擷取指定元素的第一個元素。若不滿足條件,則抛出NoSuchElementException異常
  • firstOrNull() : 擷取第一個元素,若集合為空集合,傳回null
  • firstOrNull{} : 擷取指定元素的第一個元素。若不滿足條件,傳回null
  • getOrElse(index,{…}) : 同elementAtOrElse一樣
  • getOrNull(index) : 同elementAtOrNull一樣
  • last () : 同first()相反
  • last {} : 同first{}相反
  • lastOrNull{} : 同firstOrNull()相反
  • lastOrNull() : 同firstOrNull{}相反
  • indexOf(元素) : 傳回指定元素的下标,若不存在,則傳回-1
  • indexOfFirst{…} : 傳回第一個滿足條件元素的下标,若不存在,則傳回-1
  • indexOfLast{…} : 傳回最後一個滿足條件元素的下标,若不存在,則傳回-1
  • single() : 若集合的長度等于0,則抛出NoSuchElementException異常,若等于1,則傳回第一個元素。反之,則抛出IllegalArgumentException異常
  • single{} : 找到集合中滿足條件的元素,若元素滿足條件,則傳回該元素。否則會根據不同的條件,抛出異常。這個方法慎用
  • singleOrNull() : 若集合的長度等于1,則傳回第一個元素。否則,傳回null
  • singleOrNull{} : 找到集合中滿足條件的元素,若元素滿足條件,則傳回該元素。否則傳回null
  • forEach{…} : 周遊元素。一般用作元素的列印
  • forEachIndexed{index,value} : 周遊元素,可獲得集合中元素的下标。一般用作元素以及下标的列印
  • componentX() : 這個函數在前面的章節中提過多次了。用于擷取元素。其中的X隻能代表1…5。詳情可看下面的例子

eg:

val list = listOf("kotlin","Android","Java","PHP","Python","IOS")

println(list.contains("JS"))   // false

println(list.elementAt(2))  // Java
println(list.elementAtOrElse(10,{it}))   // 10 
println(list.elementAtOrNull(10))   // null

println(list.get(2))  //  Java
println(list.getOrElse(10,{it}))  // 10
println(list.getOrNull(10))  // null

println(list.first())   //  kotlin
println(list.first{ it == "Android" })  // Android
println(list.firstOrNull())   // kotlin
println(list.firstOrNull { it == "Android" })   // Android

println(list.last())   // IOS
println(list.last{ it == "Android" })   // Android
println(list.lastOrNull())  // IOS
println(list.lastOrNull { it == "Android" })  // Android

println(list.indexOf("Android"))  // 1
println(list.indexOfFirst { it == "Android" })  //  1
println(list.indexOfLast { it == "Android" })   //  1

val list2 = listOf("list")
// 隻有當集合隻有一個元素時,才去用這個函數,不然都會抛出異常。
println(list2.single())      // list
 //當集合中的元素滿足條件時,才去用這個函數,不然都會抛出異常。若滿足條件傳回該元素
println(list2.single { it == "list" })   // list
// 隻有當集合隻有一個元素時,才去用這個函數,不然都會傳回null。
println(list2.singleOrNull())    // list
//當集合中的元素滿足條件時,才去用這個函數,不然傳回null。若滿足條件傳回該元素
println(list2.singleOrNull { it == "list" })   // list 

list.forEach { println(it) } 
// kotlin
// Android
// Java
// PHP
// Python
// IOS

list.forEachIndexed { index, it -> println("index : $index \t value = $it") }
/**
index : 0 	 value = kotlin
index : 1 	 value = Android
index : 2 	 value = Java
index : 3 	 value = PHP
index : 4 	 value = Python
index : 5 	 value = IOS
**/

  // 等價于`list[0]  <=> list.get(0)`
println(list.component1())    //  kotlin
....
 // 等價于`list[4]  <=> list.get(4)`
println(list.component5())   // Python

           

3.2 順序操作符

  • reversed() : 反序。
  • sorted() : 升序。
  • sortedBy{} : 根據條件升序。把不滿足條件的放在前面,滿足條件的放在後面
  • sortedDescending() : 降序。
  • sortedByDescending{} : 根據條件降序。

eg:

val list1 = listOf(-1,-2,1,3,5,6,7,2,4)
    // 反序
    println(list1.reversed())    // [4, 2, 7, 6, 5, 3, 1, -2, -1]
    // 升序
    println(list1.sorted())   // [-2, -1, 1, 2, 3, 4, 5, 6, 7]
    // 根據條件升序,即把不滿足條件的放在前面,滿足條件的放在後面
    println(list1.sortedBy { it % 2 == 0})  // [-1, 1, 3, 5, 7, -2, 6, 2, 4]
    // 降序
    println(list1.sortedDescending())   // [7, 6, 5, 4, 3, 2, 1, -1, -2]
    // 根據條件降序,和`sortedBy{}`相反
    println(list1.sortedByDescending { it % 2 == 0 })   // [-2, 6, 2, 4, -1, 1, 3, 5, 7]
           

3.3 映射操作符

  • map{…} : 把每個元素按照特定的方法進行轉換,組成新的集合。
  • mapNotNull{…} : 同map{}函數的作用相同,過濾掉轉換之後為null的元素
  • mapIndexed{index,result} : 把每個元素按照特定的方法進行轉換,隻是其可以操作元素的下标(index),組成新的集合。
  • mapIndexedNotNull{index,result} : 同mapIndexed{}函數的作用相同,隻是過濾掉轉換之後為null的元素
  • flatMap{…} : 根據條件合并兩個集合,組成新的集合。
  • groupBy{…} : 分組。即根據條件把集合拆分為為一個Map<K,List>類型的集合。

eg:

val list1 = listOf("kotlin","Android","Java","PHP","JavaScript")

    println(list1.map { "str-".plus(it) })
	//[str-kotlin, str-Android, str-Java, str-PHP, str-JavaScript]

    println(list1.mapNotNull { "str-".plus(it) })
    // [str-kotlin, str-Android, str-Java, str-PHP, str-JavaScript]
    
    println(list1.mapIndexed { index, str ->
        index.toString().plus("-").plus(str)
    })
    //[0-kotlin, 1-Android, 2-Java, 3-PHP, 4-JavaScript]
    
    println(list1.mapIndexedNotNull { index, str ->
        index.toString().plus("-").plus(str)
    })
    // [0-kotlin, 1-Android, 2-Java, 3-PHP, 4-JavaScript]
    
    println( list1.flatMap { listOf(it,"new-".plus(it)) })
    //[kotlin, new-kotlin, Android, new-Android, Java, new-Java, PHP, new-PHP, JavaScript, new-JavaScript]
    
    println(list1.groupBy { if (it.startsWith("Java")) "big" else "latter" })
    // {latter=[kotlin, Android, PHP], big=[Java, JavaScript]}
           

3.4 過濾操作符

  • filter{…} : 把不滿足條件的元素過濾掉
  • filterIndexed{…} : 和filter{}函數作用類似,可以操作集合中元素的下标
  • filterNot{…} : 和filter{}函數的作用相反
  • filterNotNull() : 過濾掉集合中為null的元素。
  • take(num) : 傳回集合中前num個元素組成的集合
  • takeWhile{…} : 循環周遊集合,從第一個元素開始周遊集合,當第一個出現不滿足條件元素的時候,退出周遊。然後把滿足條件所有元素組成的集合傳回。
  • takeLast(num) : 傳回集合中後num個元素組成的集合
  • takeLastWhile{…} : 循環周遊集合,從最後一個元素開始周遊集合,當第一個出現不滿足條件元素的時候,退出周遊。然後把滿足條件所有元素組成的集合傳回。
  • drop(num) : 過濾集合中前num個元素
  • dropWhile{…} : 相同條件下,和執行takeWhile{…}函數後得到的結果相反
  • dropLast(num) : 過濾集合中後num個元素
  • dropLastWhile{…} : 相同條件下,和執行takeLastWhile{…}函數後得到的結果相反
  • distinct() : 去除重複元素
  • distinctBy{…} : 根據操作元素後的結果去除重複元素
  • slice : 過濾掉所有不滿足執行下标的元素。

eg:

val list1 = listOf(-1,-3,1,3,5,6,7,2,4)
    val list2 = listOf(1,3,4,5,null,6,null,10)
    val list3 = listOf(1,1,5,2,2,6,3,3,7,4)

    println(list1.filter { it > 1  })   // [3, 5, 6, 7, 2, 4]
    println(list1.filterIndexed { index, result ->
        index < 5 && result > 3
    })
	//  [5] 

    println(list1.filterNot { it > 1 })   //  [-1, -3, 1]
    println(list2.filterNotNull())   //   [1, 3, 4, 5, 6, 10]

    println(list1.take(5))    //  [-1, -3, 1, 3, 5]
    println(list1.takeWhile { it < 5 })   //  [-1, -3, 1, 3]
    println(list1.takeLast(5))   //  [5, 6, 7, 2, 4]
    println(list1.takeLastWhile { it > 5 })   //  []

    println(list1.drop(5))   // [6, 7, 2, 4]
    println(list1.dropWhile { it < 5 })   //  [5, 6, 7, 2, 4]
    println(list1.dropLast(5))  // [-1, -3, 1, 3]
    println(list1.dropLastWhile { it > 5 })  // [-1, -3, 1, 3, 5, 6, 7, 2, 4]

    println(list3.distinct())  //  [1, 5, 2, 6, 3, 7, 4]
    println(list3.distinctBy { it + 2 })  //  [1, 5, 2, 6, 3, 7, 4]

    println(list1.slice(listOf(1,3,5,7)))  //  [-3, 3, 6, 2]
    println(list1.slice(IntRange(1,5)))  //   [-3, 1, 3, 5, 6]
           

3.5 生産操作符

  • plus() : 合并兩個集合中的元素,組成一個新的集合。也可以使用符号+
  • zip : 由兩個集合按照相同的下标組成一個新集合。該新集合的類型是:List
  • unzip : 和zip的作用相反。把一個類型為List的集合拆分為兩個集合。看下面的例子
  • partition : 判斷元素是否滿足條件把集合拆分為有兩個Pair組成的新集合

eg:

val list1 = listOf(1,2)
    val list2 = listOf("kotlin","Android","Java")

    println(list1.plus(list2))  // [1, 2, kotlin, Android, Java]
    println(list1 + list2)   //  [1, 2, kotlin, Android, Java]
	// 組成的新集合由元素少的原集合決定
    println(list1.zip(list2))   //  [(1, kotlin), (2, Android)]
	// 組成的新集合由元素少的原集合決定
    println(list1.zip(list2){
        it1,it2-> it1.toString().plus("-").plus(it2)
    })  //  [1-kotlin, 2-Android]
    
    val newList = listOf(Pair(1,"Kotlin"),Pair(2,"Android"),Pair(3,"Java"))
    println(newList.unzip())   //  ([1, 2, 3,], [Kotlin, Android, Java])

    println(list2.partition { it.startsWith("Ja") })  //  ([Java], [kotlin, Android])
           

3.6 統計操作符

  • any() : 判斷是不是一個集合,若是,則在判斷集合是否為空,若為空則傳回false,反之傳回true,若不是集合,則傳回hasNext
  • any{…} : 判斷集合中是否存在滿足條件的元素。若存在則傳回true,反之傳回false
  • all{…} : 判斷集合中的所有元素是否都滿足條件。若是則傳回true,反之則傳回false
  • none() : 和any()函數的作用相反
  • none{…} : 和all{…}函數的作用相反
  • max() : 擷取集合中最大的元素,若為空元素集合,則傳回null
  • maxBy{…} : 擷取方法處理後傳回結果最大值對應那個元素的初始值,如果沒有則傳回null
  • min() : 擷取集合中最小的元素,若為空元素集合,則傳回null
  • minBy{…} : 擷取方法處理後傳回結果最小值對應那個元素的初始值,如果沒有則傳回null
  • sum() : 計算出集合元素累加的結果。
  • sumBy{…} : 根據元素運算操作後的結果,然後根據這個結果計算出累加的值。
  • sumByDouble{…} : 和sumBy{}相似,不過sumBy{}是操作Int類型資料,而sumByDouble{}操作的是Double類型資料
  • average() : 擷取平均數
  • reduce{…} : 從集合中的第一項到最後一項的累計操作。
  • reduceIndexed{…} : 和reduce{}作用相同,隻是其可以操作元素的下标(index)
  • reduceRight{…} : 從集合中的最後一項到第一項的累計操作。
  • reduceRightIndexed{…} : 和reduceRight{}作用相同,隻是其可以操作元素的下标(index)
  • fold{…} : 和reduce{}類似,但是fold{}有一個初始值
  • foldIndexed{…} : 和reduceIndexed{}類似,但是foldIndexed{}有一個初始值
  • foldRight{…} : 和reduceRight{}類似,但是foldRight{}有一個初始值
  • foldRightIndexed{…} : 和reduceRightIndexed{}類似,但是foldRightIndexed{}有一個初始值

eg:

val list1 = listOf(1,2,3,4)
    println(list1.any())   // true
    println(list1.any{it > 10})   //  false
    println(list1.all { it > 2 })   //  false
    println(list1.none())   // false
    println(list1.none{ it > 2})    //  false
    println(list1.max())   //  4
    println(list1.maxBy { it + 2 })   // 4
    println(list1.min())    //  1
    println(list1.minBy { it + 2 })   //  1
    println(list1.sum())   //  10
    println(list1.sumBy { it + 2 })   // 18
    println(list1.sumByDouble { it.toDouble() })   //  10.0
    println(list1.average())    //  2.5
    println(list1.reduce { result, next -> result  + next})   //  10
    println(list1.reduceIndexed { index, result, next ->
        index + result + next
    })  //   16
    println(list1.reduceRight { result, next -> result  + next })   //   10
    println(list1.reduceRightIndexed {index, result, next ->
        index + result + next
    })  //  13
    println(list1.fold(3){result, next -> result  + next})  //  13
    println(list1.foldIndexed(3){index,result, next ->
        index + result  + next
    })  //  19
    println(list1.foldRight(3){result, next -> result  + next})   //  13
    println(list1.foldRightIndexed(3){index,result, next ->
        index + result  + next
    })  //  19