commonMain.kotlin.collections.Collections.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-stdlib Show documentation
Show all versions of kotlin-stdlib Show documentation
Kotlin Standard Library for JVM
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:kotlin.js.JsFileName("CollectionsKt")
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("CollectionsKt")
@file:OptIn(kotlin.experimental.ExperimentalTypeInference::class, kotlin.js.ExperimentalJsFileName::class)
package kotlin.collections
import kotlin.contracts.*
import kotlin.random.Random
internal object EmptyIterator : ListIterator {
override fun hasNext(): Boolean = false
override fun hasPrevious(): Boolean = false
override fun nextIndex(): Int = 0
override fun previousIndex(): Int = -1
override fun next(): Nothing = throw NoSuchElementException()
override fun previous(): Nothing = throw NoSuchElementException()
}
internal object EmptyList : List, Serializable, RandomAccess {
private const val serialVersionUID: Long = -7390468764508069838L
override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
override fun hashCode(): Int = 1
override fun toString(): String = "[]"
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
override fun contains(element: Nothing): Boolean = false
override fun containsAll(elements: Collection): Boolean = elements.isEmpty()
override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
override fun indexOf(element: Nothing): Int = -1
override fun lastIndexOf(element: Nothing): Int = -1
override fun iterator(): Iterator = EmptyIterator
override fun listIterator(): ListIterator = EmptyIterator
override fun listIterator(index: Int): ListIterator {
if (index != 0) throw IndexOutOfBoundsException("Index: $index")
return EmptyIterator
}
override fun subList(fromIndex: Int, toIndex: Int): List {
if (fromIndex == 0 && toIndex == 0) return this
throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
}
private fun readResolve(): Any = EmptyList
}
internal fun Array.asCollection(): Collection = ArrayAsCollection(this, isVarargs = false)
private class ArrayAsCollection(val values: Array, val isVarargs: Boolean) : Collection {
override val size: Int get() = values.size
override fun isEmpty(): Boolean = values.isEmpty()
override fun contains(element: T): Boolean = values.contains(element)
override fun containsAll(elements: Collection): Boolean = elements.all { contains(it) }
override fun iterator(): Iterator = values.iterator()
// override hidden toArray implementation to prevent copying of values array
public fun toArray(): Array = values.copyToArrayOfAny(isVarargs)
}
/**
* Returns an empty read-only list. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.emptyReadOnlyList
*/
public fun emptyList(): List = EmptyList
/**
* Returns a new read-only list of given elements. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.readOnlyList
*/
public fun listOf(vararg elements: T): List = if (elements.size > 0) elements.asList() else emptyList()
/**
* Returns a new read-only list containing only the specified object [element].
*
* The returned list is serializable (JVM).
*
* @sample samples.collections.Collections.Lists.singletonReadOnlyList
*/
@SinceKotlin("1.9")
public expect fun listOf(element: T): List
/**
* Returns an empty read-only list. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.emptyReadOnlyList
*/
@kotlin.internal.InlineOnly
public inline fun listOf(): List = emptyList()
/**
* Returns an empty new [MutableList].
* @sample samples.collections.Collections.Lists.emptyMutableList
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun mutableListOf(): MutableList = ArrayList()
/**
* Returns an empty new [ArrayList].
* @sample samples.collections.Collections.Lists.emptyArrayList
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun arrayListOf(): ArrayList = ArrayList()
/**
* Returns a new [MutableList] with the given elements.
* @sample samples.collections.Collections.Lists.mutableList
*/
public fun mutableListOf(vararg elements: T): MutableList =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
/**
* Returns a new [ArrayList] with the given elements.
* @sample samples.collections.Collections.Lists.arrayList
*/
public fun arrayListOf(vararg elements: T): ArrayList =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
/**
* Returns a new read-only list either of single given element, if it is not null, or empty list if the element is null. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.listOfNotNull
*/
public fun listOfNotNull(element: T?): List = if (element != null) listOf(element) else emptyList()
/**
* Returns a new read-only list only of those given elements, that are not null. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.listOfNotNull
*/
public fun listOfNotNull(vararg elements: T?): List = elements.filterNotNull()
/**
* Creates a new read-only list with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each list element sequentially starting from the first one.
* It should return the value for a list element given its index.
*
* @sample samples.collections.Collections.Lists.readOnlyListFromInitializer
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun List(size: Int, init: (index: Int) -> T): List = MutableList(size, init)
/**
* Creates a new mutable list with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each list element sequentially starting from the first one.
* It should return the value for a list element given its index.
*
* @sample samples.collections.Collections.Lists.mutableListFromInitializer
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun MutableList(size: Int, init: (index: Int) -> T): MutableList {
val list = ArrayList(size)
repeat(size) { index -> list.add(init(index)) }
return list
}
/**
* Builds a new read-only [List] by populating a [MutableList] using the given [builderAction]
* and returning a read-only list with the same elements.
*
* The list passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside of the function produces an unspecified behavior.
*
* The returned list is serializable (JVM).
*
* @sample samples.collections.Builders.Lists.buildListSample
*/
@SinceKotlin("1.6")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun buildList(@BuilderInference builderAction: MutableList.() -> Unit): List {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return buildListInternal(builderAction)
}
@PublishedApi
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
internal expect inline fun buildListInternal(builderAction: MutableList.() -> Unit): List
/**
* Builds a new read-only [List] by populating a [MutableList] using the given [builderAction]
* and returning a read-only list with the same elements.
*
* The list passed as a receiver to the [builderAction] is valid only inside that function.
* Using it outside of the function produces an unspecified behavior.
*
* The returned list is serializable (JVM).
*
* [capacity] is used to hint the expected number of elements added in the [builderAction].
*
* @throws IllegalArgumentException if the given [capacity] is negative.
*
* @sample samples.collections.Builders.Lists.buildListSampleWithCapacity
*/
@SinceKotlin("1.6")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun buildList(capacity: Int, @BuilderInference builderAction: MutableList.() -> Unit): List {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return buildListInternal(capacity, builderAction)
}
@PublishedApi
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
internal expect inline fun buildListInternal(capacity: Int, builderAction: MutableList.() -> Unit): List
/**
* Returns an [IntRange] of the valid indices for this collection.
* @sample samples.collections.Collections.Collections.indicesOfCollection
*/
public val Collection<*>.indices: IntRange
get() = 0..size - 1
/**
* Returns the index of the last item in the list or -1 if the list is empty.
*
* @sample samples.collections.Collections.Lists.lastIndexOfList
*/
public val List.lastIndex: Int
get() = this.size - 1
/**
* Returns `true` if the collection is not empty.
* @sample samples.collections.Collections.Collections.collectionIsNotEmpty
*/
@kotlin.internal.InlineOnly
public inline fun Collection.isNotEmpty(): Boolean = !isEmpty()
/**
* Returns `true` if this nullable collection is either null or empty.
* @sample samples.collections.Collections.Collections.collectionIsNullOrEmpty
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun Collection?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.isEmpty()
}
/**
* Returns this Collection if it's not `null` and the empty list otherwise.
* @sample samples.collections.Collections.Collections.collectionOrEmpty
*/
@kotlin.internal.InlineOnly
public inline fun Collection?.orEmpty(): Collection = this ?: emptyList()
/**
* Returns this List if it's not `null` and the empty list otherwise.
* @sample samples.collections.Collections.Lists.listOrEmpty
*/
@kotlin.internal.InlineOnly
public inline fun List?.orEmpty(): List = this ?: emptyList()
/**
* Returns this collection if it's not empty
* or the result of calling [defaultValue] function if the collection is empty.
*
* @sample samples.collections.Collections.Collections.collectionIfEmpty
*/
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun C.ifEmpty(defaultValue: () -> R): R where C : Collection<*>, C : R {
contract {
callsInPlace(defaultValue, InvocationKind.AT_MOST_ONCE)
}
return if (isEmpty()) defaultValue() else this
}
/**
* Checks if all elements in the specified collection are contained in this collection.
*
* Allows to overcome type-safety restriction of `containsAll` that requires to pass a collection of type `Collection`.
* @sample samples.collections.Collections.Collections.collectionContainsAll
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // false warning, extension takes precedence in some cases
@kotlin.internal.InlineOnly
public inline fun <@kotlin.internal.OnlyInputTypes T> Collection.containsAll(elements: Collection): Boolean = this.containsAll(elements)
/**
* Returns a new list with the elements of this list randomly shuffled
* using the specified [random] instance as the source of randomness.
*/
@SinceKotlin("1.3")
public fun Iterable.shuffled(random: Random): List = toMutableList().apply { shuffle(random) }
internal fun List.optimizeReadOnlyList() = when (size) {
0 -> emptyList()
1 -> listOf(this[0])
else -> this
}
/**
* Searches this list or its range for the provided [element] using the binary search algorithm.
* The list is expected to be sorted into ascending order according to the Comparable natural ordering of its elements,
* otherwise the result is undefined.
*
* If the list contains multiple elements equal to the specified [element], there is no guarantee which one will be found.
*
* `null` value is considered to be less than any non-null value.
*
* @return the index of the element, if it is contained in the list within the specified range;
* otherwise, the inverted insertion point `(-insertion point - 1)`.
* The insertion point is defined as the index at which the element should be inserted,
* so that the list (or the specified subrange of list) still remains sorted.
* @sample samples.collections.Collections.Lists.binarySearchOnComparable
* @sample samples.collections.Collections.Lists.binarySearchWithBoundaries
*/
public fun > List.binarySearch(element: T?, fromIndex: Int = 0, toIndex: Int = size): Int {
rangeCheck(size, fromIndex, toIndex)
var low = fromIndex
var high = toIndex - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val cmp = compareValues(midVal, element)
if (cmp < 0)
low = mid + 1
else if (cmp > 0)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
/**
* Searches this list or its range for the provided [element] using the binary search algorithm.
* The list is expected to be sorted into ascending order according to the specified [comparator],
* otherwise the result is undefined.
*
* If the list contains multiple elements equal to the specified [element], there is no guarantee which one will be found.
*
* `null` value is considered to be less than any non-null value.
*
* @return the index of the element, if it is contained in the list within the specified range;
* otherwise, the inverted insertion point `(-insertion point - 1)`.
* The insertion point is defined as the index at which the element should be inserted,
* so that the list (or the specified subrange of list) still remains sorted according to the specified [comparator].
* @sample samples.collections.Collections.Lists.binarySearchWithComparator
*/
public fun List.binarySearch(element: T, comparator: Comparator, fromIndex: Int = 0, toIndex: Int = size): Int {
rangeCheck(size, fromIndex, toIndex)
var low = fromIndex
var high = toIndex - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val cmp = comparator.compare(midVal, element)
if (cmp < 0)
low = mid + 1
else if (cmp > 0)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
/**
* Searches this list or its range for an element having the key returned by the specified [selector] function
* equal to the provided [key] value using the binary search algorithm.
* The list is expected to be sorted into ascending order according to the Comparable natural ordering of keys of its elements.
* otherwise the result is undefined.
*
* If the list contains multiple elements with the specified [key], there is no guarantee which one will be found.
*
* `null` value is considered to be less than any non-null value.
*
* @return the index of the element with the specified [key], if it is contained in the list within the specified range;
* otherwise, the inverted insertion point `(-insertion point - 1)`.
* The insertion point is defined as the index at which the element should be inserted,
* so that the list (or the specified subrange of list) still remains sorted.
* @sample samples.collections.Collections.Lists.binarySearchByKey
*/
public inline fun > List.binarySearchBy(
key: K?,
fromIndex: Int = 0,
toIndex: Int = size,
crossinline selector: (T) -> K?
): Int =
binarySearch(fromIndex, toIndex) { compareValues(selector(it), key) }
// do not introduce this overload --- too rare
//public fun List.binarySearchBy(key: K, comparator: Comparator, fromIndex: Int = 0, toIndex: Int = size(), selector: (T) -> K): Int =
// binarySearch(fromIndex, toIndex) { comparator.compare(selector(it), key) }
/**
* Searches this list or its range for an element for which the given [comparison] function returns zero using the binary search algorithm.
*
* The list is expected to be sorted so that the signs of the [comparison] function's return values ascend on the list elements,
* i.e. negative values come before zero and zeroes come before positive values.
* Otherwise, the result is undefined.
*
* If the list contains multiple elements for which [comparison] returns zero, there is no guarantee which one will be found.
*
* @param comparison function that returns zero when called on the list element being searched.
* On the elements coming before the target element, the function must return negative values;
* on the elements coming after the target element, the function must return positive values.
*
* @return the index of the found element, if it is contained in the list within the specified range;
* otherwise, the inverted insertion point `(-insertion point - 1)`.
* The insertion point is defined as the index at which the element should be inserted,
* so that the list (or the specified subrange of list) still remains sorted.
* @sample samples.collections.Collections.Lists.binarySearchWithComparisonFunction
*/
public fun List.binarySearch(fromIndex: Int = 0, toIndex: Int = size, comparison: (T) -> Int): Int {
rangeCheck(size, fromIndex, toIndex)
var low = fromIndex
var high = toIndex - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
val cmp = comparison(midVal)
if (cmp < 0)
low = mid + 1
else if (cmp > 0)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
/**
* Checks that `from` and `to` are in
* the range of [0..size] and throws an appropriate exception, if they aren't.
*/
private fun rangeCheck(size: Int, fromIndex: Int, toIndex: Int) {
when {
fromIndex > toIndex -> throw IllegalArgumentException("fromIndex ($fromIndex) is greater than toIndex ($toIndex).")
fromIndex < 0 -> throw IndexOutOfBoundsException("fromIndex ($fromIndex) is less than zero.")
toIndex > size -> throw IndexOutOfBoundsException("toIndex ($toIndex) is greater than size ($size).")
}
}
@PublishedApi
@SinceKotlin("1.3")
internal expect fun checkIndexOverflow(index: Int): Int
@PublishedApi
@SinceKotlin("1.3")
internal expect fun checkCountOverflow(count: Int): Int
@PublishedApi
@SinceKotlin("1.3")
internal fun throwIndexOverflow() { throw ArithmeticException("Index overflow has happened.") }
@PublishedApi
@SinceKotlin("1.3")
internal fun throwCountOverflow() { throw ArithmeticException("Count overflow has happened.") }
internal fun collectionToArrayCommonImpl(collection: Collection<*>): Array {
if (collection.isEmpty()) return emptyArray()
val destination = arrayOfNulls(collection.size)
val iterator = collection.iterator()
var index = 0
while (iterator.hasNext()) {
destination[index++] = iterator.next()
}
return destination
}
internal fun collectionToArrayCommonImpl(collection: Collection<*>, array: Array): Array {
if (collection.isEmpty()) return terminateCollectionToArray(0, array)
val destination = if (array.size < collection.size) {
arrayOfNulls(array, collection.size)
} else {
array
}
val iterator = collection.iterator()
var index = 0
while (iterator.hasNext()) {
@Suppress("UNCHECKED_CAST")
destination[index++] = iterator.next() as T
}
return terminateCollectionToArray(collection.size, destination)
}
/**
* In JVM if the size of [array] is bigger than [collectionSize], sets `array[collectionSize] = null`.
* In other platforms does nothing.
* Returns the given [array].
*/
internal expect fun terminateCollectionToArray(collectionSize: Int, array: Array): Array