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 interfaces
Collection
Is 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:
arrayOf
Function whose arguments are elements of the array.arrayOfNulls
The createarray () function creates an array of a given size containing null values. Commonly used to create arrays of nullable element typesArray
Constructor 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.g
intArrayOf
) 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