Description: Today is the eighth lecture in the Kotlin Shallow-talking series, in which we discuss Kotlin’s support for functional programming. We all know that functions are first-class citizens in Kotlin, so kotlin is a great language for supporting functional programming, and the syntax in Kotlin is as close to functional programming as possible. Those who have learned or understood functional programming know that the most attractive part of functional programming is that it has rich functional operators and can use a new programming way to manipulate set data. Among them, the most popular operators are the “three axe” in the function (filter, map, foldLeft/ reduce). Then you can ask the question:

  • 1. Do these operators exist in Kotlin?

A: Of course, there are many functional operators, and Kotlin’s language is a functional programming language in this respect alone.

  • 2. Are functional apis covered today?

A: Yes, today we will explain all the functional operator apis in Kotlin in detail, including basic usage, basic definition, and essential principles. We will try our best to fully analyze them

  • 3. Any tips?

Finally, a tip. Since there are many functional apis in Kotlin, some of which are not often used, it is recommended that you first understand and master the operators you use most often (I will mark them up). Others that are not commonly used can be returned later to find

Today’s content is very simple, mainly the following three points:

  • 1. A detailed introduction and complete analysis of collections in Kotlin
  • 2. Classification of functional API operators in Kotlin
  • 3, Kotlin functional API operators in detail

A detailed introduction and complete analysis of sets in Kotlin

Before introducing functional API operators, it’s worth taking a look at the collection of objects that these operators operate on. In fact, there are some differences between collections in Kotlin and Java. In Kotlin, collections fall into two main categories: mutable collections (with access and modification permissions) and read-only collections (with access permissions only). (Note: we cannot say immutable collections here, only that they have readable permissions, which was discussed in the blog earlier). One important difference between Kotlin’s collection design and Java collection design is that Kotlin separates the collection access interface from the collection modification interface.

  • 1. Why does Kotlin design collections as mutable and read-only?

The separation of var and val actually answers part of this question. Kotlin’s door is designed for the convenience of actual development and easier understanding of what happens to the data in the program. Let’s imagine a scenario in which Kotlin defines a function whose argument is a mutable set. Passing a mutable set as a parameter, in terms of Kotlin’s development rules, also indicates that a modification set operation is involved within the function body. If a read-only collection is passed as an argument, then no collection modification operations are involved within the function, only the collection is accessed. Have you fallen in love with the language? The language takes a lot of effort in all aspects and development details, making every step easier and easier to understand for developers.

  • 2. Classification of sets

The corresponding collections are included in the Kotlin.collections package. Contains Iterable and MutableIterable, Collection and MutableCollection, List and MutableList, Set and MutableSet, Map and MutableMap

  • 3. Differences and connections between mutable and read-only collections (take Collection Collection as an example)

Collection read-only sets and MutableCollectio mutable sets differ:

In a Collection, there are only methods to access elements, but no methods like Add, remove, and clear. In a MutableCollection, there are more methods to modify elements than in a Collection.

A read-only Collection is associated with a mutable Collection:

MutableCollection is actually a subinterface of the Collection interface.

  • 4. Class relationships between sets

From the collection. kt file you can see that there are sets Iterable and MutableIterable, Collection and MutableCollection, List and MutableList, Set and Mut AbleSet, Map, and MutableMap. So what’s the class diagram between them?

Iterable and MutableIterable are the parents of read-only and mutable collections, respectively. Collection inherits from Iterable. List and Set inherit from Collection. And then the MutableMap interface is inherited from Map.

  • 5. The correspondence between collections in Java and collections in Kotlin

We just said that the design of collections in Kotlin is different from that in Java, but each Kotlin interface is an instance of its corresponding Java collection interface, that is, the collection in Kotlin has some correspondence with the collection in Kotlin. The ArrayList and HashSet classes in Java are actually implementations of the MutableList and MutableSet collection interfaces in Kotlin. The class diagram above can be further refined by adding this relationship.

  • 6. Initialization of collections

Since Kotlin’s collections are mainly divided into read-only and mutable collections, the functions for initializing read-only and mutable collections are also different. For a List set, the listOf() method is used to initialize read-only sets. For a mutable set, mutableListOf() is used or ArrayList

is created. Since the internal implementation of mutableListOf() also creates an ArrayList, this ArrayList is actually java.util.arrayList

in Java, It’s just using typeAlias in Kotlin (the use of TypeAlias was described in detail in the blog earlier). About the specific contents please refer to this kind of kotlin. Collections. TypeAliasesKt implementation

  • 7. Precautions for the use of collection

Note # 1: Use read-only collections in preference everywhere in your code, and use mutable collections only when you need to modify the collection

Note 2: Read-only collections are not necessarily immutable, similar to the read-only and immutable principle for Val.

Note 3: you cannot pass a read-only collection as an argument to a function with a mutable collection.

Classification of functional API operators in Kotlin

There are many functional API operators in Kotlin, and the “three board axe” in the function must have, and the definition and usage are different. Instead of rambling rote memorization, classify these API operators in general, and then analyze, understand, and master each of them. The classification rules are divided according to the functions of each operator. The main categories of functional API operators in Kotlin are as follows.

  • 1. Filtering Operations: There are the following operators

    slice

    The filter series

    The drop series

    Take a series of

  • 2. Aggregate Operations: There are the following operators

    Any, all, count, none

    A fold series

    The forEach series

    Max series

    Min series

    Reduce series

    The sum series

  • 3. Mapping operations: There are the following operators

    FlatMap series

    GroupBy series

    The map series

  • 4. Element operations: There are mainly the following operators

    ElementAt series

    The first series

    Find a series of

    IndexOf series

    The last series

    Single series

  • 5. The Ordering operations are the following operators

    reverse

    Sort series

  • 6, Generation operations: there are mainly the following operators

    partition

    Plus series

    Zip series

Filtering Operations

Slice the operator

  • 1. Basic definitions

The slice operator, as its name implies, “slices”, meaning that it can take a portion of a set or an element of a set and eventually combine it into a new set. It has two overloaded functions, one passing in an IntRange object specifying the starting and ending positions of the slice, and the last cutting out a range of elements to add to the new collection. The other is to pass in an Iterable set of subscripts, which cuts out the corresponding elements from the specified subscripts and puts them into the new set.

  • 2, source code definition
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: 可迭代<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
}
Copy the code
  • 3, source code analysis

First, the slice function is an extension of List<T>. It has two overloaded functions, one that receives an IntRange object, the other a collection object that receives element subscripts, and the final function that returns a List<T> collection. The implementation of the function that receives an IntRange object is very simple. It is used to get the corresponding start and end positions from the IntRange object, then subList to get the subset, and finally return the subset. The function for receiving a set of subscripts is to internally create a new set object, then iterate through the original set to add elements from the set of subscripts to the newly created set, and finally return the new set object.

  • 4. Schematic diagram

  • 5. Application scenarios

Slice by IntRange Common usage scenarios: Used to slice a subset of a subscript range

The slice by itertar index is used to slice a set of one or more subscript elements

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 ")}}Copy the code

The filter and filterTo operators

  • 1. Basic Definitions:

Filters the data in the collection based on user-defined criteria and generates a new collection. This new set is a subset of the original set.

  • 2, source code definition:
public inline fun <T>可迭代<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
}
Copy the code
  • 3, source code analysis:

First, as a whole, filter is an extension of Iterable<T> and an inline function that takes a lambda predicate that takes T and returns a Boolean, so it’s also a higher-order function. Return a List<T> collection

The internal implementation then calls another function, filterTo, passing in the newly created ArrayList<T>() mutable collection object, and continues to pass the lambda expression as an argument to the filterTo function, where the actual filtering operation is performed. The predicate that’s passed in is essentially the filter that’s passed in by the external caller, and you can see that inside the filterTo there’s a for loop that filters the predicate to determine if it meets the lambda expression conditions, ArrayList<T> is added to the ArrayList<T> new collection object passed by filter calling filterTo, and the ArrayList<T> new collection object is returned. Therefore, the filter is still a set.

  • 4. Principle Diagram:

  • 5. Usage Scenarios:

The filter operator can be used to filter elements from a collection and return them as a new collection.

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// Filter out even numbers
    }

    newNumberList.forEach { print("$it   ")}}Copy the code

The filterTo operator can be used to filter elements from multiple collections, and finally collect elements from each collection with a collection.

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)
    
    // The first argument to filterTo, destination, is a mutable collection type, so we use mutableListOf initialization
    val newNumberList = mutableListOf<Int>().apply {
        numberList1.filterTo(this) {
            it % 2= =0
        }
        numberList2.filterTo(this) {
            it % 2= =0
        }
        numberList3.filterTo(this) {
            it % 2= =0
        }
    }

    print("Even set filtered from three sets:")
    newNumberList.forEach {
        print("$it   ")}}Copy the code

FilterIndexed and filterIndexedTo operators

  • 1. Basic Definitions:

The filterIndexed operator is defined almost identically to a filter. The only difference is that the lambda expression of the filterIndexed filter exposes one more parameter that is the element’s index in the set. That is, the external can get this element and the index of this element. This is especially suitable for the case that requires the collection element index to participate in the filter condition.

  • 2, source code definition:
public inline fun <T>可迭代<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>可迭代<T>.forEachIndexed(action: (index: Int, T) -> Unit) :Unit {
    var index = 0
    for (item in this) action(index++, item)
}

Copy the code
  • 3, source code analysis:

First of all, there are two operators involved to understand how filterIndexed is implemented: filterIndexedTo and forEachIndexed. As a whole, filterIndexed is an extended function of Iterable<T> and an inline function that takes a lambda predicate that takes Int and T as arguments and returns a Boolean, So it’s also a higher-order function that returns a List<T> collection.

However, the principle of most implementations is similar to filter, filterIndexedTo is similar to filterIndexed, the only thing that can be said is index, index is actually an iterated auto-increment counter inside forEachIndexed, The counter will increment itself once and the index will be called back to the outside.

  • 4. Usage Scenarios:
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 // Filter out the first five elements of the set that are even
    }

    newNumberList.forEach {
        print("$it  ")}}Copy the code

The filterIsInstance and filterIsInstanceTo operators

  • 1. Basic definitions

The filterIsInstance operator is a specific application of the filter operator that filters instance elements of a particular type from a collection, casts the element to that type, and returns the collection of elements.

  • 2, source code definition
public inline fun <reified R>可迭代< * >.filterIsInstance(a): 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
}
Copy the code
  • 3, source code analysis

First, filterIsInstance is an extension function. The main implementation of filterIsInstanceTo is to create an ArrayList mutable collection of R generics, which is used to collect elements of instance R type from the original collection, via an R generics passed in from outside. You can see inside filterIsInstanceTo is iterating through the collection and then using IS to determine that elements of type R are added to the collection, and then return the collection.

  • 4. Application scenarios

FilterInstance applies to a collection of abstract classes with multiple subtypes of elements. It is convenient to filter the elements of the corresponding subtypes and return them as a collection.

FilterInstanceTo is basically the same as filterInstance, but the only difference is that the mutable collection ArrayList

is created externally rather than internally, which is great for filtering multiple collections.

Let’s take a look at an example of how not using filterInstance compares with using filterInstance.

Instead of using filterInstance, use a combination of filter and map collections. (If you don’t know about the filterInstance operator, there are probably many implementations of this.)

abstract class Animal(var name: String, var age: Int) {abstract fun eatFood(a): String
}
class Bird(name: String, age: Int): Animal(name, age){
    override fun eatFood(a) = "bird eat worm"
}
class Cat(name: String, age: Int) : Animal(name, age) {
    override fun eatFood(a) = "Cat eat Fish"
}
class Dog(name: String, age: Int) : Animal(name, age) {
    override fun eatFood(a) = "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))Filter out all Dog information using the filter and map operators
    animalList.filter {
        it is Dog
    }.map {
        it as Dog
    }.forEach {
        println("${it.name} is ${it.age} years old, and ${it.eatFood()}")}}Copy the code

Use the filterInstance operator

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))Filter out all Dog information using the filterIsInstance operator
    animalList.filterIsInstance<Dog>().forEach { println("${it.name} is ${it.age} years old, and ${it.eatFood()}")}}Copy the code

The filterNot and filterNotTo operators

  • 1. Basic definitions

The filter operator reverses the filter operator by filtering out elements from a collection that do not match the criteria and returning them as a new collection.

  • 2, source code definition
public inline fun <T>可迭代<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
}
Copy the code
  • 3, source code analysis

In fact, filterNot does not have anything to say about it. It also uses filterNotTo to specify the operation. The only difference between filterTo and filterTo is that the condition is opposite

  • 4. Schematic diagram

  • 5. Application scenarios

The usage scenario is the filter filter filter, of course, you can continue to use the filter operator, and filter conditions to filter conditions.

The filterNotNull and filterNotNullTo operators

  • 1. Basic definitions

The filterNotNull operator can filter null elements in a set, so filterNotNullTo is a true filter, but requires passing in a mutable set from the outside.

  • 2, source code definition
public fun <T : Any>可迭代
       ?>.filterNotNull(a): List<T> {
    return filterNotNullTo(ArrayList<T>())
}

public fun <C : MutableCollection<in T>, T : Any> Iterable
       ?>.filterNotNullTo(destination: C): C {
    for (element in this) if(element ! =null) destination.add(element)
    return destination
}
Copy the code
  • 3, source code analysis

FilterNotNull is an extension function of a set whose elements are nullable T generics. FilterNotNull still passes in a mutable set, and then determines within filterNotNullTo that the null element is filtered directly, and other elements are added to the mutable set passed in.

  • 4. Application scenarios

The filterNotNull operator is used to filter out null elements from a collection and return a collection of null elements.

The filterNotNullTo operator is used to pass in a mutable set externally, filter null elements from multiple sets, place those elements in the mutable set, and return the set.

fun main(args: Array<String>) {
    valanimalList: 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()}")}}Copy the code

Drop the operator

  • 1. Basic definitions

According to the incoming value n, represents the sequential deletion of the elements in n sets from left to right, and returns the remaining elements in the set.

  • 2, source code definition
public fun <T>可迭代<T>.drop(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return toList()// The element to be deleted is 0, indicating that the rest of the set of elements is exactly the entire set
    val list: ArrayList<T>// Declare a mutable collection
    if (this is Collection<*>) {// If the original Collection is a read-only Collection or subclass, then the size of the original Collection is determinable, and the size of the new Collection can be calculated by the difference calculation
        val resultSize = size - n// Take the difference between the size of the original collection and the starting subscript to determine the size of the returned collection resultSize
        if (resultSize <= 0)// The size of the collection is less than or equal to 0
            return emptyList()
        if (resultSize == 1)//resultSize = 1 returns the last element of the original collection
            return listOf(last())
        list = ArrayList<T>(resultSize)// Create a variable collection of resultSize
        if (this is List<T>) {
            if (this is RandomAccess) {//RandomAccess is a collection markup interface. If the collection class is an implementation of RandomAccess, try to iterate with index indices rather than iterators, which is less efficient. Conversely, if the List is a Sequence List, it is best to iterate with iterators.
                for (index in n until size)// use subscript traversal
                    list.add(this[index])
            } else {
                for (item in listIterator(n))// use iterators to traverse
                    list.add(item)
            }
            return list
        }
    }
    else {// If the original collection is a mutable collection, then the exact size of the new collection cannot be calculated.
        list = ArrayList<T>()
    }
    var count = 0
    for (item in this) {
        if (count++ >= n) list.add(item)// For mutable sets, add elements to the set when the counter exceeds the initial index.
    }
    return list.optimizeReadOnlyList()
}
Copy the code
  • 3. Schematic diagram

  • The scene drop operator is generally used to remove part of the set elements. Drop is a sequential deletion. N indicates that several elements are removed in sequence and the set of remaining elements is returned
fun main(args: Array<String>) {
    val numberList = listOf(1.2.3.4.5.6.7.8.9)
    numberList.drop(5).forEach { print("$it   ")}}Copy the code

DropLast operator

  • 1. Basic definitions

Deletes the elements of n sets in reverse order from right to left, according to the incoming value n, and returns the remaining elements of the set.

  • 2, source code definition
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 should be this.take(),this refers to List, and (size-n) must be greater than or equal to 0
}

// This is an extension function of type Int that checks whether a value is greater than the default minimum passed in, and returns the default minimum otherwise
public fun Int.coerceAtLeast(minimumValue: Int): Int {
    return if (this < minimumValue) minimumValue else this
}

//take is also an operator
public fun <T>可迭代<T>.take(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()// Where n is the difference between size and dropLast, n = 0 means that dropLast passes in n as the original set size, which means that dropLast removes the original set size
    if (this is Collection<T>) {// If it is a read-only collection, you can determine the size of the collection
        if (n >= size) return toList()// If n = size means that dropLast passed in n = 0, the number of elements removed from the collection is 0, and the entire collection is left
        if (n == 1) return listOf(first())// If n is equal to 1, dropLasr will pass in n as sie-1, then it will delete the number of sets sie-1, since the order of deletion is in reverse order.
    }
    // This is a mutable collection, since the size of the mutable collection is not easy to determine, so another way to implement dropLast functionality.
    var count = 0
    val list = ArrayList<T>(n)// Create a variable set of remaining set elements size n
    for (item in this) {// Add a new set from left to right, and wait until the count counter increases to n.
        if (count++ == n)
            break
        list.add(item)
    }
    return list.optimizeReadOnlyList()
}

Copy the code
  • 3. Schematic diagram

  • 4. Application scenarios

The scenario used is the opposite of drop, but the overall effect is similar to drop.

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

DropWhile operator

  • 1. Basic definitions

Start with the first item in the set and remove the element that meets the condition. This operation continues until the first element that does not meet the condition appears. Return the remaining element (the remaining element may have the element that meets the condition).

  • 2, source code definition
public inline fun <T>可迭代<T>.dropWhile(predicate: (T) -> Boolean): List<T> {
    var yielding = false// Initialize the flag bit false
    val list = ArrayList<T>()// Create a new mutable collection
    for (item in this)// iterate over the original collection
        if (yielding)// The flag remains false until it is set to true to start adding elements to the new collection if the external pass condition of the lambda expression is not met
            list.add(item)
        else if(! predicate(item)) {// Start adding elements to the new set, set the tag to true,
        // This satisfies the requirement that the elements that meet the condition are not added to the new collection at first, and the new collection is added only when the elements that do not meet the condition are not added to the new collection
            list.add(item)
            yielding = true
        }
    return list
}
Copy the code
  • 3. Schematic diagram

  • 4. Application scenarios

Applies to scenes where the first half of the set has the same characteristics.

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  ")}}Copy the code

DropLastWhile operator

  • 1. Basic definitions

Start with the last item in the set and remove the element that meets the condition. This operation continues until the first element that does not meet the condition appears. Return the remaining element (the remaining element may have the element that meets the condition).

  • 2, source code definition
public inline fun <T> List<T>.dropLastWhile(predicate: (T) -> Boolean): List<T> {
    if(! isEmpty()) {val iterator = listIterator(size)// iterates from the end of the original set to the head
        while (iterator.hasPrevious()) {// The current element has the previous element to enter the iteration
            if(! predicate(iterator.previous())) {// Add the next element to the new set until the last element does not meet the criteria
                return take(iterator.nextIndex() + 1)}}}return emptyList()
}
Copy the code
  • 3. Schematic diagram

  • 4. Application scenarios

The scenario used is similar to dropWhile, except that the elements are removed in a different order

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  ")}}Copy the code

Take the operator

  • 1. Basic definitions

We take the elements of the set starting with the first item in the original set, we take n elements, and we return the set that we took those elements from. In other words, take the first n elements of the set and return a new set.

  • 2, source code definition
public fun <T>可迭代<T>.take(n: Int): List<T> {
    require(n >= 0) { "Requested element count $n is less than zero." }
    if (n == 0) return emptyList()// if n is 0, the set of zero elements is returned
    if (this is Collection<T>) {// If the collection is read-only, the size of the collection can be determined
        if (n >= size) return toList()// If you want the set size to be greater than or equal to the original set size, return the original set
        if (n == 1) return listOf(first())// Take 1 element from the first item, so it is set first().
    }
    var count = 0
    val list = ArrayList<T>(n)// Create a variable set of n sizes
    for (item in this) {// iterate over the original collection
        if (count++ == n)// If the increment counter count exceeds the number of elements to be fetched, the loop is broken
            break
        list.add(item)
    }
    return list.optimizeReadOnlyList()
}
Copy the code
  • 3. Schematic diagram

  • 4. Application scenarios

It applies to taking the set of neutrons sequentially from the first term

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

TakeLast operator

  • 1. Basic definitions

You take the elements of the set in reverse order from the last item in the set, you take n elements, and you return the set where you took those elements.

  • 2, source code definition
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()// if n is 0, the set of zero elements is returned
    val size = size
    if (n >= size) return toList()// Return the entire collection if the size of the collection is greater than size
    if (n == 1) return listOf(last())Return last(); // Return last();
    val list = ArrayList<T>(n)// create a variable set of size N
    if (this is RandomAccess) {//RandomAccess is a collection markup interface. If the collection class is an implementation of RandomAccess, try to iterate with index indices rather than iterators, which is less efficient. Conversely, if the List is a Sequence List, it is best to iterate with iterators.
        for (index in size - n until size)// Use the following traversal
            list.add(this[index])
    } else {
        for (item in listIterator(size - n))// use iterators to traverse
            list.add(item)
    }
    return list
}
Copy the code
  • 3. Schematic diagram

  • 4. Application scenarios

It works in reverse order by taking the set of neutrons from the last term

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

TakeLastWhile operator

  • 1. Basic definitions

Start from the last item of the set to extract the element that meets the condition. This operation continues until the first element that does not meet the condition appears.

  • 2, source code definition
public inline fun <T> List<T>.takeLastWhile(predicate: (T) -> Boolean): List<T> {
    if (isEmpty())// If the current collection is empty, return the empty collection directly
        return emptyList()
    val iterator = listIterator(size)// iterating from set index = size, then size-1 is the last element of the iterator, which is the end of the set and previous
    while (iterator.hasPrevious()) {// The element containing the previous element continues into the iteration
        if(! predicate(iterator.previous())) {// Do not proceed to the following operation until the first element of an element fails to meet the condition, and the first element fails to meet the condition starting from the last element
            iterator.next()
            val expectedSize = size - iterator.nextIndex()Since the iteration starts at the tail, the set of qualifying elements expectedSize is the difference between the original set size and the index of the current next element
            if (expectedSize == 0) return emptyList()// If the difference is 0, the iteration at the end of the original set is not qualified and terminated, so return empty set
            return ArrayList<T>(expectedSize).apply {[expectedSize] // Get the set size that fits the criteria, create a new set of expectedSize larger and smaller, and add the iterator traversal to the new set
                while (iterator.hasNext())
                    add(iterator.next())
            }
        }
    }
    return toList()
}
Copy the code
  • 3, source code analysis

The takeLastWhile operator, an extended inline function of a collection, is also a higher-order function that takes a Lambda expression that returns a Boolean in return for a T generic argument, which is the implementation of the condition that takeLastWhile takes the element.

  • 4. Schematic diagram

  • 5. Application scenarios

Applies to scenes where the latter half of the collection has the same characteristics.

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

TakeWhile operator

  • 1. Basic definitions

Start from the first item of the set to extract the element that meets the condition, so the operation continues until the first element that does not meet the condition appears, suspends fetching the element, and returns to the set of removed elements.

  • 2, source code definition
public inline fun <T>可迭代<T>.takeWhile(predicate: (T) -> Boolean): List<T> {
    val list = ArrayList<T>()// Create a mutable collection
    for (item in this) {// iterate over the original collection
        if(! predicate(item))// If you do not meet the incoming criteria, you can jump out of the training directly
            break
        list.add(item)// Those that meet the criteria are added directly to the new set
    }
    return list// Finally return the new collection
}
Copy the code
  • 3, source code analysis

The takeWhile operator is an extended inline function of a collection. It is also a higher-order function that takes a Lambda expression that returns a Boolean in return for a T generic argument, which is the implementation of the condition that takeWhile takes the element. Iterate over the entire original set, and add those that meet the conditions to the new set. Once encountering unqualified elements, jump out of the loop directly, that is, terminate the operation of fetching elements when encountering the first unqualified element, and finally return the new set.

  • 4. Schematic diagram

  • 5. Application scenarios

Applies to scenes where the first half of the set has the same characteristics.

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

Finally, due to the limited space of this article, the last article only analyzed the functional API operators of the filter class in detail, and the next article will continue to analyze the other operators. Please continue to pay attention to ~~~

Welcome to the Kotlin Developer Association, where the latest Kotlin technical articles are published, and a weekly Kotlin foreign technical article is translated from time to time. If you like Kotlin, welcome to join us ~~~