1. 程式人生 > >Kotlin系列之集合和函式式API完全解析-上篇

Kotlin系列之集合和函式式API完全解析-上篇

簡述: 今天帶來的是Kotlin淺談系列的第八講,這講我們一起來聊聊Kotlin這門語言對函數語言程式設計的支援。我們都知道在kotlin這門語言中函式榮升成為了一等公民,所以在支援函數語言程式設計的方面,Kotlin這門語言也是非常給力的,並且在Kotlin中語法也儘量推薦接近函數語言程式設計的風格。學過以及瞭解過函數語言程式設計的小夥伴都知道函數語言程式設計最吸引人的地方,莫過於它擁有豐富的函式式操作符,可以使用一種全新的程式設計方式去操作集合資料。其中操作符最流行莫過於函式式中“三板斧”(過濾filter、對映map、摺疊foldLeft/化約reduce)。那麼小夥伴會提問了:

  • 1、那Kotlin語言中有這些操作符嗎?

答: 當然有,不僅有這些而且還有很多很豐富的函式式操作符,僅從這方面來說Kotlin這門語言是函數語言程式設計語言一點也不為過。

  • 2、那今天是講函式式API嗎?

答: 沒錯,今天會對Kotlin中所有函式式操作符API做詳細的講解,包括基本使用、基本定義、實質原理三個方面來做介紹,力求做到完全解析

  • 3、有什麼小建議?

最後,一個小建議,由於Kotlin中的函式式API有很多,有些也不是經常使用的,建議先將經常使用的操作符(我會做好標記)理解、掌握。其他不常用的後續可以返回來查詢即可

今天闡述的內容點很簡單,主要有以下三點:

  • 1、Kotlin中集合的詳細介紹和完全解析
  • 2、Kotlin中函式式API操作符的分類
  • 3、Kotlin中函式式API操作符的詳解

一、Kotlin中集合的詳細介紹和完全解析

在介紹函式式API操作符之前,有必要去了解一下這些操作符操作的物件集合。實際上,Kotlin語言中的集合和Java還是有一定區別的。在Kotlin中集合主要分為了兩個大類,一類是可變集合(具有訪問和修改許可權),另一類是只讀集合(只具有訪問許可權)(注意: 這裡不能說不可變集合,只能說是具有可讀許可權,關於這個不可變和可讀討論之前部落格有相關闡述)。Kotlin的集合設計與Java集合設計有一個很重要區別就是Kotlin把集合訪問介面和集合修改介面分開了。

  • 1、Kotlin為什麼把集合設計為可變和只讀兩種?

關於這個問題,實際上之前的var和val的分離設計已經回答了一部分。Kotlin這門考慮到實際開發中方便和程式中資料發生的事情更容易讓人理解,所以才有此設計。我們設想一下這樣的場景,kotlin中定義一個函式,函式的引數是一個可變集合,以kotlin開發規則而言,傳遞一個可變集合作為引數,實際上也在表明在該函式體內部涉及到修改集合操作。如果傳遞的是一個只讀集合作為引數,那麼表明在該函式體內是不會涉及到修改集合操作,只允許訪問集合。看到如此的設計你是否已經愛上了這門語言,也就是這門語言在各個方面和開發細節上都是花了很多功夫的,力求做到任何一步都是讓開發者開發更簡單和更容易理解。

  • 2、集合的分類

在kotlin.collections包中包含相應集合。主要包含Iterable(只讀迭代器)和MutableIterable(可變迭代器)、Collection和MutableCollection、List和MutableList、Set和MutableSet、Map和MutableMap

  • 3、可變集合與只讀集合之間的區別和聯絡(以Collection集合為例)

Collection只讀集合與MutableCollectio可變集合區別:

在Collection只具有訪問元素的方法,不具有類似add、remove、clear之類的方法,而在MutableCollection中則相比Collection多出了修改元素的方法。

Collection只讀集合與MutableCollectio可變集合聯絡:

MutableCollection實際上是Collection集合介面的子介面,他們之間是繼承關係。

  • 4、集合之間類的關係

通過Collection.kt檔案中可以瞭解到有這些集合Iterable(只讀迭代器)和MutableIterable(可變迭代器)、Collection和MutableCollection、List和MutableList、Set和MutableSet、Map和MutableMap。那麼它們之間的類關係圖是怎樣的。

Iterable和MutableIterable介面分別是隻讀和可變集合的父介面,Collection繼承Iterable然後List、Set介面繼承自Collection,Map介面比較特殊它是單獨的介面,然後MutableMap介面是繼承自Map.

  • 5、Java中的集合與Kotlin中集合對應關係

我們剛剛說到在Kotlin中集合的設計與Java不一樣,但是每一個Kotlin的介面都是其對應的Java集合介面的一個例項,也就是在Kotlin中集合與Kotlin中的集合存在一定的對應關係。Java中的ArrayList類和HashSet類實際上Kotlin中的MutableList和MutableSet集合介面的實現類。把這種關係加上,上面的類關係圖可以進一步完善。

  • 6、集合的初始化

由於在Kotlin中集合主要分為了只讀集合和可變集合,那麼初始化只讀集合和可變集合的函式也不一樣。以List集合為例,對於只讀集合初始化一般採用listOf()方法對於可變集合初始化一般採用mutableListOf()或者直接建立ArrayList\

二、Kotlin中函式式API操作符的分類

Kotlin中函式式API操作符有很多,函式式中“三板斧”必須有的,定義和用法也是不盡相同。與其雜亂的死記硬背,不如先從大體上給這些API操作符分類,然後針對每一類去分析、理解、掌握,分類的規則也是按照各個操作符的功能來分。Kotlin中函式式API操作符主要有以下幾大類。

  • 1、篩選類操作符(Filtering operations):主要有以下操作符

    slice

    filter系列

    drop系列

    take系列

  • 2、並集類操作符(Aggregate operations):主要有以下操作符

    any、all、count、none

    fold系列

    forEach系列

    max系列

    min系列

    reduce系列

    sum系列

  • 3、對映類操作符(Mapping operations):主要有以下操作符

    flatMap系列

    groupBy系列

    map系列

  • 4、元素類操作符(Element operations):主要有以下操作符

    elementAt系列

    first系列

    find系列

    indexOf系列

    last系列

    single系列

  • 5、排序類操作符(Ordering operations):主要有以下操作符

    reverse

    sort系列

  • 6、生成類操作符(Generation operations):主要有以下操作符

    partition

    plus系列

    zip系列

三、篩選類函式式API的詳解(Filtering operations)

slice操作符

  • 1、基本定義

slice操作符顧名思義是”切片“的意思,也就是它可以取集合中一部分元素或者某個元素,最後也是組合成一個新的集合。它有兩個過載函式,一個傳入IntRange物件指定切片起始位置和終止位置,最後切出的是一個範圍的元素加入到新集合中。另一個是傳入一個Iterable下標集合,也就會從指定下標分別切出對應的元素,最後放入到新集合中。

  • 2、原始碼定義
public fun <T> List<T>.slice(indices: IntRange): List<T> {
    if (indices.isEmpty()) return listOf()
    return this.subList(indices.start, indices.endInclusive + 1).toList()
}

public fun <T> List<T>.slice(indices: Iterable<Int>): List<T> {
    val size = indices.collectionSizeOrDefault(10)
    if (size == 0) return emptyList()
    val list = ArrayList<T>(size)
    for (index in indices) {
        list.add(get(index))
    }
    return list
}
  • 3、原始碼解析

首先,slice函式是List\

fun main(args: Array<String>) {
    val numberList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)

    val newNumberList1 = numberList.slice(IntRange(3, 6))
    print("slice by IntRange: ")
    newNumberList1.forEach {
        print("$it ")
    }

    println()

    val newNumberList2 = numberList.slice(listOf(1, 3, 7))
    print("slice by iterator index: ")
    newNumberList2.forEach {
        print("$it ")
    }
}

filter和filterTo操作符

  • 1、基本定義:

根據使用者定義的條件篩選集合中的資料,並且由此產生一個新的集合。這個新的集合是原集合的子集。

  • 2、原始碼定義:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
    return filterTo(ArrayList<T>(), predicate)
}

public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}
  • 3、原始碼解析:

首先,從整體上可以看出filter是一個Iterable\

fun main(args: Array<String>) {
    val numberList = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    val newNumberList = numberList.filter { number ->
        number % 2 == 0//篩選出偶數
    }

    newNumberList.forEach { print("$it   ")}
}

filterTo的操作符使用場景: 從多個集合篩選出符合條件的元素,並最終用一個集合進行收集從每個集合篩選出的元素。

fun main(args: Array<String>) {
    val numberList1 = listOf(23, 65, 14, 57, 99, 123, 26, 15, 88, 37, 56)
    val numberList2 = listOf(13, 55, 24, 67, 93, 137, 216, 115, 828, 317, 16)
    val numberList3 = listOf(20, 45, 19, 7, 9, 3, 26, 5, 38, 75, 46)

    //需要注意一點的是,我們從原始碼看到filterTo第一個引數destination是一個可變集合型別,所以這裡使用的mutableListOf初始化
    val newNumberList = mutableListOf<Int>().apply {
        numberList1.filterTo(this) {
            it % 2 == 0
        }
        numberList2.filterTo(this) {
            it % 2 == 0
        }
        numberList3.filterTo(this) {
            it % 2 == 0
        }
    }

    print("從三個集合篩選出的偶數集合: ")
    newNumberList.forEach {
        print("$it   ")
    }
}

filterIndexed和filterIndexedTo操作符

  • 1、基本定義:

filterIndexed操作符定義和filter幾乎是一樣的。他們之前唯一的區別是filterIndexed篩選條件的lambda表示式多暴露一個引數那就是元素在集合中的index.也就是外部可以拿到這個元素以及這個元素的index. 特別適合需要集合元素index參與篩選條件的case。

  • 2、原始碼定義:
public inline fun <T> Iterable<T>.filterIndexed(predicate: (index: Int, T) -> Boolean): List<T> {
    return filterIndexedTo(ArrayList<T>(), predicate)
}

public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterIndexedTo(destination: C, predicate: (index: Int, T) -> Boolean): C {
    forEachIndexed { index, element ->
        if (predicate(index, element)) destination.add(element)
    }
    return destination
}

public inline fun <T> Iterable<T>.forEachIndexed(action: (index: Int, T) -> Unit): Unit {
    var index = 0
    for (item in this) action(index++, item)
}
  • 3、原始碼解析:

首先,要了解filterIndexed實現原理還需要涉及兩個操作符: filterIndexedTo、forEachIndexed。從整體上可以看出filterIndexed是一個Iterable\

fun main(args: Array<String>) {
    val numberList = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    val newNumberList = numberList.filterIndexed { index, number ->
        index < 5 && number % 2 == 0 //篩選出集合中前五個元素中是偶數的數
    }

    newNumberList.forEach {
        print("$it  ")
    }
}

filterIsInstance和filterIsInstanceTo操作符

  • 1、基本定義

filterIsInstance操作符是filter操作符一個特定應用,從集合中篩選出instance某個特定型別元素並把該元素強轉成該型別,最後返回這些元素集合。

  • 2、原始碼定義
public inline fun <reified R> Iterable<*>.filterIsInstance(): List<@kotlin.internal.NoInfer R> {
    return filterIsInstanceTo(ArrayList<R>())
}

public inline fun <reified R, C : MutableCollection<in R>> Iterable<*>.filterIsInstanceTo(destination: C): C {
    for (element in this) if (element is R) destination.add(element)
    return destination
}
  • 3、原始碼解析

首先,filterIsInstance是一個擴充套件函式,它的主要實現是藉助於filterIsInstanceTo,通過外部傳入的R泛型,建立一個R泛型的ArrayList可變集合,用於收集原集合中instance R型別的元素.可以看出在filterIsInstanceTo內部是遍歷集合然後利用is判斷屬於R型別的元素就加入到集合中,最後返回該集合。

  • 4、使用場景

filterInstance使用場景: 適用於一個抽象類集合中還有多種子型別的元素,可以很方便篩選對應子型別的元素,並組成一個集合返回。

filterInstanceTo使用場景:
基本作用和filterInstance一致,不過唯一的區別就是這個可變集合ArrayList\

abstract class Animal(var name: String, var age: Int){
    abstract fun eatFood(): String
}
class Bird(name: String, age: Int): Animal(name, age){
    override fun eatFood() = "bird eat worm"
}
class Cat(name: String, age: Int) : Animal(name, age) {
    override fun eatFood() = "Cat eat Fish"
}
class Dog(name: String, age: Int) : Animal(name, age) {
    override fun eatFood() = "dog eat bone"
}

fun main(args: Array<String>) {
    val animalList: List<Animal> = listOf(Bird(name = "Bird1", age = 12),
            Cat(name = "Cat1", age = 18),
            Cat(name = "Cat3", age = 20),
            Dog(name = "Dog2", age = 8),
            Cat(name = "Cat2", age = 8),
            Bird(name = "Bird2", age = 14),
            Bird(name = "Bird3", age = 16),
            Dog(name = "Dog1", age = 18)
    )

    //篩選出個所有Dog的資訊,藉助filter和map操作符
    animalList.filter {
        it is Dog
    }.map {
        it as Dog
    }.forEach {
        println("${it.name} is ${it.age} years old, and ${it.eatFood()}")
    }
}

使用filterInstance操作符實現

fun main(args: Array<String>) {
    val animalList: List<Animal> = listOf(Bird(name = "Bird1", age = 12),
            Cat(name = "Cat1", age = 18),
            Cat(name = "Cat3", age = 20),
            Dog(name = "Dog2", age = 8),
            Cat(name = "Cat2", age = 8),
            Bird(name = "Bird2", age = 14),
            Bird(name = "Bird3", age = 16),
            Dog(name = "Dog1", age = 18)
    )

    //篩選出個所有Dog的資訊,藉助filterIsInstance操作符
    animalList.filterIsInstance<Dog>().forEach { println("${it.name} is ${it.age} years old, and ${it.eatFood()}") }
}

filterNot和filterNotTo操作符

  • 1、基本定義

從一個集合篩選出符合條件之外的元素,並以一個新集合返回,它是filter操作符取反操作。

  • 2、原始碼定義
public inline fun <T> Iterable<T>.filterNot(predicate: (T) -> Boolean): List<T> {
    return filterNotTo(ArrayList<T>(), predicate)
}
public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterNotTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (!predicate(element)) destination.add(element)
    return destination
}
  • 3、原始碼解析

實際上filterNot沒什麼可說的,它也是藉助於filterNotTo操作具體,和filterTo唯一區別就是判斷條件取反

  • 4、原理圖解

  • 5、使用場景

使用場景就是filter使用的取反條件使用,當然你也可以繼續使用filter操作符,並且篩選條件為取反條件。

filterNotNull和filterNotNullTo操作符

  • 1、基本定義

filterNotNull操作符可以過濾集合中為null的元素,那麼同理filterNotNullTo才是真正過濾操作,但是需要從外部傳入一個可變集合。

  • 2、原始碼定義
public fun <T : Any> Iterable<T?>.filterNotNull(): List<T> {
    return filterNotNullTo(ArrayList<T>())
}

public fun <C : MutableCollection<in T>, T : Any> Iterable<T?>.filterNotNullTo(destination: C): C {
    for (element in this) if (element != null) destination.add(element)
    return destination
}
  • 3、原始碼解析

filterNotNull是集合的擴充套件函式,該集合中的元素是可null的T泛型,那麼這個篩選條件也就是判斷是否為null,篩選條件內部確定好的。filterNotNull還是繼續傳入一個可變集合,然後在filterNotNullTo內部判斷把null的元素直接過濾,其他元素就會被加入傳入的可變集合中。

  • 4、使用場景

filterNotNull操作符使用場景: 一般用於過濾掉集合中為null的元素,最後返回一個不含null的元素集合。

filterNotNullTo操作符使用場景: 一般在外部傳入一個可變的集合,然後過濾多個集合中為null的元素,最後將這些元素放入可變集合中,並返回這個集合。

fun main(args: Array<String>) {
    val animalList: List<Animal?> = listOf(Bird(name = "Bird1", age = 12),
            Cat(name = "Cat1", age = 18),
            Cat(name = "Cat3", age = 20),
            Dog(name = "Dog2", age = 8),
            null,
            Bird(name = "Bird2", age = 14),
           null,
            Dog(name = "Dog1", age = 18)
    )

    animalList.filterNotNull().forEach { println("${it.name} is ${it.age} years old and it ${it.eatFood()}") }
}

drop操作符

  • 1、基本定義

根據傳入數值n,表示從左到右順序地刪除n個集合中的元素,並返回集合中剩餘的元素。

  • 2、原始碼定義
public fun <T> Iterable<T>.drop(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return toList()//要刪除元素為0,說明剩餘元素集合正好取整個集合
    val list: ArrayList<T>//宣告一個可變集合
    if (this is Collection<*>) {//如果原集合是一個只讀的Collection或者其子類,那麼原集合的size是可確定的,那麼建立新集合size是可以做差計算得到的
        val resultSize = size - n//拿原集合的size與起始下標做差值確定最終返回的集合的大小resultSize
        if (resultSize <= 0)//集合的size小於或等於0直接返回空集合
            return emptyList()
        if (resultSize == 1)//resultSize等於1說明就直接返回原集合的最後一個元素
            return listOf(last())
        list = ArrayList<T>(resultSize)//建立resultSize大小的可變集合
        if (this is List<T>) {
            if (this is RandomAccess) {//RandomAccess是一個集合標記介面,如果集合類是RandomAccess的實現,則儘量用index下標 來遍歷而不要用Iterator迭代器來遍歷,在效率上要差一些。反過來,如果List是Sequence List,則最好用迭代器來進行迭代。
                for (index in n until size)//採用下標遍歷
                    list.add(this[index])
            } else {
                for (item in listIterator(n))//採用迭代器遍歷
                    list.add(item)
            }
            return list
        }
    }
    else {//如果原集合是一個可變的集合,那麼就無法通過計算確切的新集合的size。
        list = ArrayList<T>()
    }
    var count = 0
    for (item in this) {
        if (count++ >= n) list.add(item)//對於可變集合通過遍歷,計數累加的方式,當計數器超過起始下標就開始往集合中新增元素。
    }
    return list.optimizeReadOnlyList()
}
  • 3、原理圖解

  • 4、使用場景
    drop操作符一般是適用於把集合元素去除一部分,drop是順序的刪除,n則表示順序刪除幾個元素,最後返回剩餘元素集合
fun main(args: Array<String>) {
    val numberList = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
    numberList.drop(5).forEach { print("$it   ") }
}

dropLast操作符

  • 1、基本定義

根據傳入數值n,表示從右到左倒序地刪除n個集合中的元素,並返回集合中剩餘的元素。

  • 2、原始碼定義
public fun <T> List<T>.dropLast(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    return take((size - n).coerceAtLeast(0))//這裡應該是this.take(),this指代List,然後傳入(size - n)必須滿足大於或等於0
}

//這是一個Int型別的擴充套件函式,用於判斷某個值是否大於傳入預設最小值,如果大於就直接返回這個值,否則返回這個預設最小值
public fun Int.coerceAtLeast(minimumValue: Int): Int {
    return if (this < minimumValue) minimumValue else this
}

//take也是一種操作符
public fun <T> Iterable<T>.take(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()//這裡n 實際上是size - dropLast傳入n的差值,n為0表示dropLast傳入n為原集合size,相當於刪除原集合size個數元素,那麼剩下就是空集合了
    if (this is Collection<T>) {//如果是一個只讀型別集合,就可以確定該集合的size
        if (n >= size) return toList()//如果這裡n等於size表示dropLast傳入n為0,那麼表示刪除集合元素個數為0,那麼剩下來就是整個原集合了
        if (n == 1) return listOf(first())//如果n等於1,表示dropLasr傳入n為size-1,那麼表示刪除集合個數size-1個,由於刪除順序是倒序的,自然原集合剩下的元素就是第一個元素了。
    }
    //以下是針對this是一個可變集合,由於可變集合的size不太好確定,所以採用另一方式實現dropLast功能。
    var count = 0
    val list = ArrayList<T>(n)//建立剩餘集合元素size大小n的可變集合
    for (item in this) {//由於是從右到左遞增刪除的,取剩餘,現在是採用逆向方式,從左到右加入新的集合中,一直等待count計數器自增到n為止。
        if (count++ == n)
            break
        list.add(item)
    }
    return list.optimizeReadOnlyList()
}
  • 3、原理圖解

  • 4、使用場景

使用的場景和drop相反,但是整體作用和drop類似。

fun main(args: Array<String>) {
    val strList = listOf("kotlin", "java", "javaScript", "C", "C++", "python", "Swift", "Go", "Scala")
    strList.dropLast(3).forEach { print("$it   ") }
}

dropWhile操作符

  • 1、基本定義

從集合的第一項開始去掉滿足條件元素,這樣操作一直持續到出現第一個不滿足條件元素出現為止,返回剩餘元素(可能剩餘元素有滿足條件的元素)

  • 2、原始碼定義
public inline fun <T> Iterable<T>.dropWhile(predicate: (T) -> Boolean): List<T> {
    var yielding = false//初始化標誌位false
    val list = ArrayList<T>()//建立一個新的可變集合
    for (item in this)//遍歷原集合
        if (yielding)//該標誌一直為false直到,不符合lambda表示式外部傳入條件時,該標記為置為true,才開始往新集合新增元素
            list.add(item)
        else if (!predicate(item)) {//判斷不符合外部傳入的條件,才開始往新集合新增元素,標記置為true,
        //這樣就滿足了需求,一開始符合條件元素不會被新增到新集合中,不符合條件才開始加入新集合,這樣產生新集合相對於原集合而言也就是刪除符合條件元素直到出現不符合條件的為止
            list.add(item)
            yielding = true
        }
    return list
}
  • 3、原理圖解

  • 4、使用場景

適用於去掉集合中前半部分具有相同特徵的元素場景。

fun main(args: Array<String>) {
    val strList = listOf("java", "javaScript", "kotlin", "C", "C++", "javaFx","python", "Swift", "Go", "Scala")
    strList.dropWhile { it.startsWith("java") }.forEach { print("$it  ") }
}

dropLastWhile操作符

  • 1、基本定義

從集合的最後一項開始去掉滿足條件元素這樣操作一直持續到出現第一個不滿足條件元素出現為止,返回剩餘元素(可能剩餘元素有滿足條件的元素)

  • 2、原始碼定義
public inline fun <T> List<T>.dropLastWhile(predicate: (T) -> Boolean): List<T> {
    if (!isEmpty()) {
        val iterator = listIterator(size)//表示從原集合尾部開始向頭部迭代
        while (iterator.hasPrevious()) {//當前元素存在上一個元素進入迭代
            if (!predicate(iterator.previous())) {//直到出現上一個元素不符合條件,才開始取相應後續元素,加入到新集合中
                return take(iterator.nextIndex() + 1)
            }
        }
    }
    return emptyList()
}
  • 3、原理圖解

  • 4、使用場景

使用的場景和dropWhile類似,不過刪除元素順序不一樣

fun main(args: Array<String>) {
    val strList = listOf("java", "javaScript", "kotlin", "C", "C++", "javaFx", "python","Go", "Swift", "Scala")
    strList.dropLastWhile { it.startsWith("S") }.forEach { print("$it  ") }
}

take操作符

  • 1、基本定義

從原集合的第一項開始順序取集合的元素,取n個元素,最後返回取出這些元素的集合。換句話說就是取集合前n個元素組成新的集合返回。

  • 2、原始碼定義
public fun <T> Iterable<T>.take(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()//n為0表示取0個元素的集合,返回空集合
    if (this is Collection<T>) {//如果是隻讀集合,可確定集合的size
        if (n >= size) return toList()//如果要取元素集合大小大於或等於原集合大小那麼就直接返回原集合
        if (n == 1) return listOf(first())//從第一項開始取1個元素,所以就是集合的first()
    }
    var count = 0
    val list = ArrayList<T>(n)//建立一個n大小可變集合
    for (item in this) {//遍歷原集合
        if (count++ == n)//自增計數器count大小超過要取元素個數,就跳出迴圈
            break
        list.add(item)
    }
    return list.optimizeReadOnlyList()
}
  • 3、原理圖解

  • 4、使用場景

適用於順序從第一項開始取集合中子集合

fun main(args: Array<String>) {
    val strList = listOf("java", "javaScript", "kotlin", "C", "C++", "javaFx", "python","Go", "Swift", "Scala")
    strList.take(2).forEach { print("$it ") }
}

takeLast操作符

  • 1、基本定義

從原集合的最後一項開始倒序取集合的元素,取n個元素,最後返回取出這些元素的集合。

  • 2、原始碼定義
public fun <T> List<T>.takeLast(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()//n為0表示取0個元素的集合,返回空集合
    val size = size
    if (n >= size) return toList()//如果取的元素集合大小大於size直接返回整個集合
    if (n == 1) return listOf(last())//從最後一項開始取1個元素,自然就是返回last()
    val list = ArrayList<T>(n)//建立一個n大小的可變集合
    if (this is RandomAccess) {//RandomAccess是一個集合標記介面,如果集合類是RandomAccess的實現,則儘量用index下標 來遍歷而不要用Iterator迭代器來遍歷,在效率上要差一些。反過來,如果List是Sequence List,則最好用迭代器來進行迭代。
        for (index in size - n until size)//採用下邊遍歷
            list.add(this[index])
    } else {
        for (item in listIterator(size - n))//採用迭代器遍歷
            list.add(item)
    }
    return list
}
  • 3、原理圖解

  • 4、使用場景

適用於倒序從最後一項開始取集合中子集合

fun main(args: Array<String>) {
    val strList = listOf("java", "javaScript", "kotlin", "C", "C++", "javaFx", "python","Go", "Swift", "Scala")
    strList.takeLast(2).forEach { print("$it ") }
}

takeLastWhile操作符

  • 1、基本定義

從集合的最後一項開始取出滿足條件元素這樣操作一直持續到出現第一個不滿足條件元素出現為止,暫停取元素,返回取出元素的集合。

  • 2、原始碼定義
public inline fun <T> List<T>.takeLastWhile(predicate: (T) -> Boolean): List<T> {
    if (isEmpty())//如果當前集合是一個空的,那麼直接返回空集合
        return emptyList()
    val iterator = listIterator(size)//表示從集合index = size開始迭代,那麼size - 1也是最後一個元素,也即是迭代器的previous,也就是從集合尾部開始向頭部迭代
    while (iterator.hasPrevious()) {//含有上一個元素的元素繼續進入迭代
        if (!predicate(iterator.previous())) {//直到某個元素的前一個元素不符合條件,也是從最後一項開始遇到第一個不符合條件的元素,不進入以下操作
            iterator.next()
            val expectedSize = size - iterator.nextIndex()//由於從尾部開始迭代,那麼符合條件元素集合的expectedSize等於原集合size與當前下一個元素的index的差值
            if (expectedSize == 0) return emptyList()//差值為0的話說明,在原集合尾部開始迭代就不符合條件被終止,所以返回空集合
            return ArrayList<T>(expectedSize).apply {//拿到符合條件元素集合size,建立expectedSize大小新集合,並把迭代器中的元素遍歷加入到新集合中
                while (iterator.hasNext())
                    add(iterator.next())
            }
        }
    }
    return toList()
}
  • 3、原始碼解析

takeLastWhile操作符是一個集合的擴充套件行內函數,也是一個高階函式,它接收一個以接收T泛型引數返回一個Boolean型別的Lambda表示式,也是即是takeLastWhile取元素的條件的實現。

  • 4、原理圖解

相關推薦

Kotlin系列集合式式API完全解析-

簡述: 今天帶來的是Kotlin淺談系列的第八講,這講我們一起來聊聊Kotlin這門語言對函數語言程式設計的支援。我們都知道在kotlin這門語言中函式榮升成為了一等公民,所以在支援函數語言程式設計的方面,Kotlin這門語言也是非常給力的,並且在Kotlin中

Kotlin開發五 Kotlin集合常用式式API

在kotlin中有一些常用的對集合進行操作的函式式API,掌握他們可以大大提高程式設計效率,這些API與java8中的 Stream API類似。下面主要講解以下幾大類 1 filter 和 map filter主要用作過濾器(對集合元素),它的返回值也是一個集

Kotlin系列變數常量

標籤: Kotlin      變數      屬性     var與val區別      自定義屬性訪問器     智慧型別推斷 目錄: 簡述: Kotlin基礎系列第二篇,之前記

[譯]Effective Kotlin系列使用Sequence來優化集合的操作(四)

簡述: 今天迎來了Effective Kotlin系列的第四篇文章: 使用Sequence序列來優化大集合的頻繁操作.關於Sequence這個主題應該大家都不陌生,我寫過幾篇有關它的文章,可以說得上很詳細了。如果你對它的使用不太熟悉,歡迎檢視下面幾篇有關文章: 淺談Kotlin中的Sequences原

Kotlin系列命名引數預設引數

今天一起來看看Kotlin的命名引數和預設引數。 如果你學過Python,那你對這兩個概念一定不陌生,那我們今天就來學習一波Kotlin中的預設引數和命名引數。 遇到的問題 為了說明命名引數和預設引數出現的必要性,我們先丟擲一個問題,如何打印出

Kotlin系列迭代集合與map

今天來學習一下Kotlin中如何迭代集合和map。 還是以往的慣例,先說說Java中的方式,在對比看看Kotlin中的方式。 Java中迭代集合和map 這個屬於Java中的基礎知識,這裡貼一下程式碼,方便後面與Kotlin對比。 Java程

Kotlin系列主構造方法初始化語句塊

前面我們已經說過類和介面的知識點,今天來看看Kotlin中的主構造方法和初始化語句塊。 我們知道在Java中也存在構造方法的概念,也有初始化塊的概念。所以今天還是拿Kotlin和Java進行對比學習。 Java中的構造方法和初始化塊 在Java

Arduino通信系列print()write()輸出方式的差異

scl 監視 () nbsp 函數 1.2 系列 輸出數據 int 我們都知道,在HardwareSerial類中有print()和write()兩種輸出方式, 兩個都可以輸出數據,但其輸出方式並不相同。 例子: float FLOAT=1.23456; int INT=1

基礎數據類型集合深淺copy,還有一些數據類型補充

去重 不可變 blog 而是 ron 之間 key 哈希 內存                       集合 集合是無序的,不重復的數據集合,它裏面的元素是可哈希的(不可變類型),但是集合本身是不可哈希(所以集合做不了字典的鍵)的。以下是集合最重要的兩點:   去重,把

Python集合

報錯 定義函數 能夠 內置 lob 範圍 特性 模塊 列表 一 集合1 集合定義:A 如果花括號為空,則是字典類型B 定義一個空集合,使用set 加小括號使用B方式定義集合時,集合內部的數必須是可叠代對象,數值類型的不可以 集合的簡單應用(實現對集合內元素的去重操作,

Kotlin基礎區間型別轉換

fun main(args: Array<String>) { // 1.區間可以使用.. 表示, 注意..在使用的時候要保證起始值要小於結束值 // 輸出1 2 3 4 for (i in 1..4) { println(i) }

Kotlin語法變數函式的定義

/** * 方法的定義, a, b為變數名 * fun 方法名(變數名: 變數型別): 返回值型別 */ fun sum(a: Int, b: Int): Int { return a + b } /** * 當表示式作為函式體的時候, 可以省去返回值型別 * 由函式自己推斷

(譯)Effective Kotlin系列遇到多個構造器引數要考慮使用Builder(二)

翻譯說明: 原標題: Effective Java in Kotlin, item 2: Consider a builder when faced with many constructor parameters 原文地址: https://blog.kotlin-academy.

(譯)Effective Kotlin系列考慮使用靜態工廠方法替代構造器(一)

翻譯說明: 原標題: Effective Java in Kotlin, item 1: Consider static factory methods instead of constructors 原文地址: https://blog.kotlin-academy.com/eff

three.js入門系列視角輔助線

假設你已經建立好了three.js的開發環境(我是寫在vue專案中的),那麼接下來,從頭開始演示是如何用three.js來構建3D圖形的。(筆記本寫的程式碼,螢幕小,所以為了能夠整屏看到完整程式碼,就將字型縮小了,如果覺得看不清的,可以另行放大) 一、頁面DOM結構 正如你所見,這就是一

three.js入門系列陰影

初中物理教過我們鏡面反射和漫反射,這是由於物體的材質直接導致的。 在three.js中,由於物體的材料不同,對於光源的反應也是不一樣的,下面就讓我們一探究竟。 一、材料 據Three.js中描述,有兩種材料能對光源有所反應: 就是圖中箭頭標識的兩種材料。 二、檢驗 編輯前例,設定光源位置:

CEF3開發者系列工程程式碼結構

1、環境  版本:cef_binary_3.2171.1901_windows32  工具:cmake-3.5.0-win32-x86、 VS2008及以上均可 2、編譯  下載好CEF的原始碼後,使用Cmake生成對應的Solution,很多地方都有介紹,這邊就不再贅述了

(譯)Effective Kotlin系列探索高階函式中inline修飾符(三)

簡述: 不知道是否有小夥伴還記得我們之前的Effective Kotlin翻譯系列,之前一直忙於趕時髦研究Kotlin 1.3中的新特性。把此係列耽擱了,趕完時髦了還是得踏實探究本質和基礎,從今天開始我們將繼續探索Effective Kotlin系列,今天是Effective Kotlin第三講。 翻譯說明

spring ioc---DI進階集合特殊值的處理

DI進階之集合的使用 集合 使用 list連結串列容器 使用list和value標籤 set無序容器 使用set和value標籤 map鍵值對

[譯]Effective Kotlin系列考慮使用原始型別的陣列優化效能(五)

翻譯說明: 原標題: Effective Kotlin: Consider Arrays with primitives for performance critical processing 原文地址: blog.kotlin-academy.com/effective-k… 原文作者: Marcin