Before the order

Kotlin does not have his own collection library, relying entirely on the collection classes in the Java standard library and adding features to enhance collections through extension functions. This means that Kotlin never needs to wrap or transform these collection objects when interacting with Java, greatly increasing interoperability with Java.

Read-only and mutable collections

One of the biggest differences between Kotlin and Java is that Kotlin divides collections into read-only and mutable collections. This difference comes from the most basic Collection interface: kotlin. Collections. The Collection. This interface can perform some basic operations on collections, but does not have any methods to add and remove elements.

Only realize the kotlin. Collections. MutableCollection interface to modify the collection of data. The MutableCollection interface inherits from a Collection and provides methods to add, remove, and empty Collection elements. When a function receives a Collection instead of a MutableCollection, it does not modify the Collection.

Iterable<T>
Iterable<T>

Create a collection

Collections created in Kotlin are typically created through top-level functions in collection.kt. Specific methods are as follows:

Collection types read-only variable
List listOf MutableList, arrayListOf
Set setOf MutableSetOf, hashSetOf, linkedSetOf, sortedSetOf
Map mapOf MutableMapOf, hashMapOf, linkeMapOf, sortedMapOf

Top-level functions such as arrayListOf, which specify collection types, are created as collections corresponding to Java types. To figure out what sets of Java types Kotlin’s generated read-only collections (listOf, setOf, and mapOf) versus mutable collections (mutableList, mutableSetOf, and mutableMapOf) generate, A small experiment (corresponding to empty set, single-element set and multi-element set respectively) is conducted:

  • Write some static methods to print collection types in a Java class:
#daqiJava.java
public static void collectionsType(Collection collection){
    System.out.println(collection.getClass().getName());
}

public static void mapType(Map map){
    System.out.println(map.getClass().getName());
}
Copy the code
  • Create read-only collections and mutable collections in Kotlin and pass them into a previously declared Java static method for printing:
#daqiKotlin.kt
fun main(args: Array<String>) {
    val emptyList = listOf<Int>()
    val emptySet = setOf<Int>()
    val emptyMap = mapOf<Int,Int>()
    
    val initList = listOf(1)
    val initSet = setOf(2)
    val initMap = mapOf(1 to 1)
    
    val list = listOf(1,2)
    val set = setOf(1,2) val map = mapOf(1 to 1 to 2) println("Empty element read-only set")
    collectionsType(emptyList)
    collectionsType(emptySet)
    mapType(emptyMap)
    
    println("Single-element read-only set")
    collectionsType(initList)
    collectionsType(initSet)
    mapType(initMap)
    
    println("Multi-element read-only set")
    collectionsType(list)
    collectionsType(set)
    mapType(map)

    println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --") val emptyMutableList = mutableListOf<Int>() val emptyMutableSet = mutableSetOf<Int>() val emptyMutableMap = mutableMapOf<Int,Int>() val initMutableList = mutableListOf(1) val initMutableSet = mutableSetOf(2) val initMutableMap = MutableMapOf (1 to 1) val mutableList = mutableListOf(1,2) val mutableSet = mutableSetOf(1,2) val mutableMap = MutableMapOf (1 to 1,2 to 2) println("Mutable set of empty elements")
    collectionsType(emptyMutableList)
    collectionsType(emptyMutableSet)
    mapType(emptyMutableMap)

    println("Variable set of one element")
    collectionsType(initMutableList)
    collectionsType(initMutableSet)
    mapType(initMutableMap)

    println("Variable set of many elements")
    collectionsType(mutableList)
    collectionsType(mutableSet)
    mapType(mutableMap)
}
Copy the code

Results:

listOf
setOf
mapOf
mutableList
mutableSetOf
mutableMapOf

methods Java type
listOf() kotlin.collections.EmptyList
setOf() kotlin.collections.EmptySet
mapOf() kotlin.collections.EmptyMap
listOf(element: T) java.util.Collections$SingletonList
setOf(element: T) java.util.Collections$SingletonSet
mapOf(pair: Pair<K, V>) java.util.Collections$SingletonMap
listOf(vararg elements: T) java.util.Arrays$ArrayList
setOf(vararg elements: T) java.util.LinkedHashSet
mapOf(vararg pairs: Pair<K, V>) java.util.LinkedHashMap
mutableList() java.util.ArrayList
mutableSetOf() java.util.LinkedHashSet
mutableMapOf() java.util.LinkedHashMap

Type variable

Read-only collection types are type-variant. When the Rectangle class inherits from Shape, you can use List

anywhere you need List

. Because collection types have the same subtype relationship as element types. Map is type-variant in value types, but not in key types.

Mutable sets are not type-variant. MutableList

is a subtype of MutableList

. When you insert a successor of another Shape (for example, Circle), you violate its Rectangle type parameter.

The nullity of sets

Any type can be declared nullable, and collections are no exception. You can set the type of a collection element to nullable, or you can set the collection itself to nullable. You need to know whether the elements of the collection are nullable or whether the collection itself is nullable.

The Secret of the Kotlin collection: platform-specific declarations

Looking for Java. Util. ArrayList

While learning Kotlin, I was often told that Kotlin was working directly with native Java collections and clicked on mutableListOf(), the top-level method for creating collections, in a quest for truth.

#Collections.kt
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
    if (elements.size == 0) 
        ArrayList() 
    else
        ArrayList(ArrayAsCollection(elements, isVarargs = true))
Copy the code

ArrayList Java ArrayList Java ArrayList Java ArrayList Java ArrayList Click on ArrayList to find an ArrayList defined by Kotlin:

#ArrayList.ktexpect class ArrayList<E> : MutableList<E>, RandomAccess { constructor() constructor(initialCapacity: Int) constructor(elements: Collection<E>) //... Omit some methods from List, MutableCollection, and MutableList. }Copy the code

No trace of Java’s ArrayList can be found after a long walk around…. Excuse me??? You promised to use Java’s ArrayList, but you created an ArrayList…. . What is the expect keyword that finally targets the class declaration? This is the expected announcement of the Kotlin platform related announcement!

Platform related Statement

In other languages, it is common to build a set of interfaces in common code and implement them in platform-specific modules to implement multiple platforms. However, this approach is not ideal when there is already a library on one of these platforms that implements the desired functionality and you want to use that library’s API directly without additional wrappers.

Kotlin provides a platform related declaration mechanism. With this mechanism, the expected declaration is defined in the common module, and the platform module provides the actual declaration corresponding to the expected declaration.

Key points:

  • The expected declaration in a public module always has exactly the same fully qualified name as its corresponding actual declaration.
  • The expected declaration is marked with the expect keyword; The actual statement is marked with the actual keyword.
  • All actual declarations that match any part of the expected declaration need to be marked actual.
  • The expected declaration never contains any implementation code.

The website provides a simple example:

#ktExpect class Foo(bar: String) {fun frob()} funmain() {
    Foo("Hello").frob()
}
Copy the code

The corresponding JVM module provides the implementation declaration and the corresponding implementation:

#ktActual class Foo Actual constructor(val bar: String) {actual funfrob() {
        println("Frobbing the $bar")}}Copy the code

If you have a platform-specific library that you want to use in common code, provide your own implementation for other platforms. (Like Java, which already provides a full collection library) you can use an alias for an existing class as an actual declaration:

expect class AtomicRef<V>(value: V) {
  fun get(): V
  fun set(value: V)
  fun getAndSet(value: V): V
  fun compareAndSet(expect: V, update: V): Boolean
}

actual typealias AtomicRef<V> = java.util.concurrent.atomic.AtomicReference<V>
Copy the code

The Java collection class is defined in TypeAliases.kt as an alias for the actual declaration. Typealiase.kt: typeAliase.kt: typeAliase.kt: typeAliase.kt: typeAliase.kt: typeAliase.kt

# TypeAliases.kt
@SinceKotlin("1.1") public actual typealias RandomAccess = java.util.RandomAccess

@SinceKotlin("1.1") public actual typealias ArrayList<E> = java.util.ArrayList<E>
@SinceKotlin("1.1") public actual typealias LinkedHashMap<K, V> = java.util.LinkedHashMap<K, V>
@SinceKotlin("1.1") public actual typealias HashMap<K, V> = java.util.HashMap<K, V>
@SinceKotlin("1.1") public actual typealias LinkedHashSet<E> = java.util.LinkedHashSet<E>
@SinceKotlin("1.1") public actual typealias HashSet<E> = java.util.HashSet<E>
Copy the code

Kotlin implements direct use of Java’s collection classes on the JVM by defining some of them as a generic layer of collections (using Expect to define the expected declarations) and using the aliases of existing Java collection classes as the actual declarations.

The changes of the ArrayList

This can be seen in the evolution of collections in Kotlin’s official documentation (ArrayList for example) :

  • Version 1.0 ArrayList:
  • Version 1.1 ArrayList:
  • Version 1.3 ArrayList:

From the original no arrayList. kt, just a set of extended properties and methods for ArrayList. Java

-> Use an alias to reference Java’s arrayList. Java, arrayList. kt serves Js modules.

-> Using platform-specific declarations, arrayList.kt is the expected declaration and provides specific actual declarations in JVM modules, Js modules, Native modules. Enable Kotlin to provide a “generic layer “API that can be cross-platform without changing the code.

Read-only collections are platform-specific declarations

When a set of values corresponds to a single or multiple set of initialized values, it uses the Java set type. Let’s explore whether this is also related to platform dependent declarations:

Single-element read-only collection

The listOf(Element: T), setOf(Element: T), and mapOf(pair: pair

) that create single-element collections are declared directly in the JVM module as top-level functions and initialized directly using Java’s single-element collection classes.
,>

#CollectionsJVM.kt
//listOf
public fun <T> listOf(element: T): List<T> =
java.util.Collections.singletonList(element)
Copy the code
#SetsJVM.kt
//setOf
public fun <T> setOf(element: T): Set<T> =
java.util.Collections.singleton(element)
Copy the code
#MapsJVM.kt
//mapOf
public fun <K, V> mapOf(pair: Pair<K, V>): Map<K, V> =
java.util.Collections.singletonMap(pair.first, pair.second)
Copy the code

Multi-element read-only collection

The arguments to top-level functions that create multi-element collections take vararg declarations, which are similar to Java’s mutable arguments, taking any number of parameter values and packaging them into arrays.

  • ListOf (vararg elements: T) :
#Collections.kt
public fun <T> listOf(vararg elements: T): List<T> = 
if (elements.size > 0) elements.asList() else emptyList()
Copy the code

The listOf(vararg elements: T) function converts variables directly to list:

#_Arrays.kt
public expect fun <T> Array<out T>.asList(): List<T>
Copy the code

Array.aslist () has the expect keyword, which exists as an expected declaration, meaning that the JVM module provides the corresponding implementation:

#_ArraysJvm.kt
public actual fun <T> Array<out T>.asList(): List<T> {
    return ArraysUtilJVM.asList(this)
}
Copy the code
#ArraysUtilJVM.java
class ArraysUtilJVM {
    static <T> List<T> asList(T[] array) {
        returnArrays.asList(array); }}Copy the code

The JVM module provides the actual declared array.asList () and calls java.util.array.aslist () to return the java.util.Arrays$ArrayList static inner class of java.util.Arrays.

  • setOf(vararg elements: T):
#Sets.kt
public fun <T> setOf(vararg elements: T): Set<T> = 
if (elements.size > 0) elements.toSet() else emptySet()
Copy the code

The setOf(vararg elements: T) function converts the variable argument directly to set:

public fun <T> Array<out T>.toSet(): Set<T> {
    return when (size) {
        0 -> emptySet()
        1 -> setOf(this[0])
        else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
    }
}
Copy the code

And like mutableSetOf(), Kotlin’s LinkedHashSet is used to create a java.util.linkedhashSet object based on platform declarations. (The specific conversion logic is not detailed)

  • mapOf(vararg pairs: Pair<K, V>)
public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> =
    if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()
Copy the code

And like mutableMapOf(), Kotlin’s LinkedHashMap is used to create a java.util.LinkedHashMap object based on platform specific declarations. (The specific conversion logic is not detailed)

The functional API for collections

With a wave of Kotlin’s collections behind us, we need to return to the use of collections — the functional APIS of collections.

The filter function

Basic Definition:

The filter function iterates through the collection and returns the elements in the given lambda that return true.

Source:

#_Collection.ktPublic inline fun <T> Iterable<T>. Filter (predicate: (T) -> Boolean): List<T> {// Create a new set and pass it with the lambda to filterTo()return filterTo(ArrayList<T>(), predicate)
}

public inline fun <T, C : MutableCollection<inT>> Iterable<T>. FilterTo (destination: C, predicate: (T) -> Boolean): C {// Iterable<T>for (element inThis) // executes a lambda, such as returning astrueThat element is added to the new collectionif(predicate(element)) destination.add(element) // Returns the new setreturn destination
}
Copy the code

Resolution:

Create a new ArrayList object, iterate over the original collection, add elements that return true from the lambda expression to the new ArrayList object, and finally return the new ArrayList object.

The map function

Basic Definition:

The map function applies the given function to each element in the collection and collects the results into a new collection.

Source:

#_Collection.ktPublic inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {// Create a new set and pass it to mapTo().return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

public inline fun <T, R, C : MutableCollection<inR>> Iterable<T>. MapTo (destination: C, transform: (T) -> R): C {// Iterable<T>for (item inDestination. add(transform(item)) // Returns the new collectionreturn destination
}
Copy the code

Resolution:

Create a new ArrayList collection, iterate over the original collection, add the values processed by the function type object to the new ArrayList object, and return the new ArrayList object.

GroupBy function

Basic Definition:

Group collection elements and return a Map collection that stores the keys and grouping of elements by which they are grouped

Source:

#_Collection.ktpublic inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> {// Create a new Map and pass it with the lambda to groupByTo()return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector)
}

public inline fun <T, K, M : MutableMap<inK, MutableList<T>>> Iterable<T>. GroupByTo (destination: M, keySelector: (T) -> K): Mfor (element inVal Key = keySelector(Element) // Get the vlaue in the new map using the resulting key. If not, create an ArrayList object. Store it as a value in the map and return an ArrayList object. Val list = destination.getorput (key) {ArrayList<T>()} // Adds the current element to the ArrayList object list.add(element)} // returns a new collectionreturn destination
}
Copy the code

Resolution:

Create a LinkedHashMap object that iterates through the elements of the old collection, takes the processed value of the function object as the key, stores the corresponding element in an ArrayList, and stores the ArrayList object as the value of the map. Return the LinkedHashMap object.

FlatMap function

Basic Definition:

Swap (map) each element in the collection according to the function given by the argument, and then tiled multiple lists into a single list.

Source:

#_Collection.ktpublic inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {// Create a new collection and pass it along with the lambda to flatMapTo()return flatMapTo(ArrayList<R>(), transform)
}

public inline fun <T, R, C : MutableCollection<inR>> Iterable<T>. FlatMapTo (destination: C, transform: (T) -> Iterable<R>): C {//// Iterable over old set elementsfor (element inVal list = transform(element) val list = transform(element) val list = transform(element) val list = transform(element) Destination. addAll(list)} // Returns a new collectionreturn destination
}
Copy the code

Resolution:

Create a new ArrayList collection, iterate over the original collection, convert the elements of the original collection to a list, and finally store the transformed list in the new ArrayList collection and return a new ArrayList object.

All and any

Basic Definition:

Checks if all elements in the collection match or if there are elements that match.

Source:

#_Collection.kt//any public inline fun <T> Iterable<T>. Any (predicate: (T) -> Boolean): Boolean {// Determine whether the set is emptyfalseBecause it certainly doesn't existif (this is Collection && isEmpty()) 
        return false
    for (element inThis) // If one of the elements meets the criteria, the value is returnedtrue
        if (predicate(element)) 
            return true// If all else fails, returnfalse
    return false} //all public inline fun <T> Iterable<T>. All (predicate: (T) -> Boolean): Boolean {// Return if the set is emptytrue
    if (this is Collection && isEmpty()) 
        return true
    for (element inThis) // If one of the elements does not meet the criteria, the element is returnedfalse
        if(! predicate(element))return false
    return true
}
Copy the code

The count function

Basic Definition:

Check the number of elements that satisfy the condition.

Source:

#_Collection.kt
public inline fun <T> Iterable<T>.count(predicate: (T) -> Boolean): Int {
    if (this is Collection && isEmpty()) 
        returnVar count = 0 // Iterate over elementsfor (element inThis) // If the addition is satisfied, the quantity +1if (predicate(element)) 
            checkCountOverflow(++count)
    return count
}
Copy the code

The find function

Basic Definition:

Finds the first element that meets the criteria, or returns NULL if none exists.

Source:

#_Collection.ktpublic inline fun <T> Iterable<T>.find(predicate: (T) -> Boolean): T? {// Pass lambda to firstOrNull()return firstOrNull(predicate)
}

public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? {
    for (element inThis) // returns the first element that matches the element added.if (predicate(element)) 
            returnElement // if not found, null is returnedreturn null
}
Copy the code

Collection of usage considerations

  • Read-only collections are preferred, and mutable collections are used only when the collection needs to be modified.
  • Read-only collections are not necessarily immutable. If you use a variable of the type of a read-only interface, the variable may refer to a mutable collection. Because read-only interfacesCollectionIs the base class of all sets.
  • Read-only collections are not always thread-safe. If you need to process data in a multithreaded environment, you must use data structures that support concurrent access.

An array of

A Kotlin array is a class with type parameters whose element types are specified as the corresponding type parameters.

Kotlin provides the following methods to create arrays:

  • arrayOfFunction whose arguments are elements of the array.
  • arrayOfNullsThe createarray () function creates an array of a given size containing null values. Commonly used to create arrays of nullable element types
  • ArrayConstructor that takes the size of an array and a lambda expression. Lambda expressions are used to create each array element; each element cannot be passed explicitly.
val array = Array<String>(5){
    it.toChar() + "a"
}
Copy the code

The most common way Kotlin creates an array is by calling a Java method that takes an array as an argument, or a Kotlin function that takes a vararg argument. ToTypeArray () is used to convert collections to arrays.

val list = listOf("daqi"."java"."kotlin"ToTypedArray () val array = arrayOf()""// Array.tolist ()Copy the code

The type parameter of the Array class determines the creation of a boxed Array of primitive data types. You must use an array of primitive data types when you want to create an array of unboxed primitive data types. Kotlin provides a separate array of primitive data types for each primitive data type. For example, an array of type Int is called IntArray. Arrays of primitive data types are compiled to arrays of ordinary Java primitive data types, such as int[]. So the basic data type array is not boxed when storing values.

Create an array of basic data types:

  • Factory approach (e.gintArrayOf) takes variable-length parameters and creates an array to store those values.
  • A constructor for an array of primitive data types.

The Kotlin standard library’s support extensions for collections (filter, Map, and so on) also apply to arrays, including arrays of primitive data types.

References:

  • Kotlin in Action
  • Kotlin website

Android Kotlin series:

Kotlin’s Knowledge generalization (I) — Basic Grammar

Kotlin knowledge generalization (2) – make functions easier to call

Kotlin’s knowledge generalization (iii) — Top-level members and extensions

Kotlin knowledge generalization (4) – interfaces and classes

Kotlin’s knowledge induction (v) — Lambda

Kotlin’s knowledge generalization (vi) — Type system

Kotlin’s knowledge induction (7) — set

Kotlin’s knowledge induction (viii) — sequence

Kotlin knowledge induction (ix) — Convention

Kotlin’s knowledge induction (10) — Delegation

Kotlin’s knowledge generalization (xi) — Higher order functions

Kotlin’s generalization of knowledge (xii) – generics

Kotlin’s Generalization of Knowledge (XIII) — Notes

Kotlin’s Knowledge induction (xiv) — Reflection