diff --git a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt index 3523cf5beaa..99ce9cb1780 100644 --- a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt +++ b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt @@ -44,14 +44,6 @@ public inline fun <@kotlin.internal.OnlyInputTypes T> MutableCollection.r public inline fun <@kotlin.internal.OnlyInputTypes T> MutableCollection.retainAll(elements: Collection): Boolean = @Suppress("UNCHECKED_CAST") (this as MutableCollection).retainAll(elements) -/** - * Removes the element at the specified [index] from this list. - * In Kotlin one should use the [MutableList.removeAt] function instead. - */ -@Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)"), level = DeprecationLevel.ERROR) -@kotlin.internal.InlineOnly -public inline fun MutableList.remove(index: Int): T = removeAt(index) - /** * Adds the specified [element] to this mutable collection. */ @@ -149,71 +141,6 @@ public fun MutableCollection.addAll(elements: Array): Boolean { return addAll(elements.asList()) } -/** - * Removes all elements from this [MutableIterable] that match the given [predicate]. - * - * @return `true` if any element was removed from this collection, or `false` when no elements were removed and collection was not modified. - */ -public fun MutableIterable.removeAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, true) - -/** - * Retains only elements of this [MutableIterable] that match the given [predicate]. - * - * @return `true` if any element was removed from this collection, or `false` when all elements were retained and collection was not modified. - */ -public fun MutableIterable.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false) - -private fun MutableIterable.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean { - var result = false - with(iterator()) { - while (hasNext()) - if (predicate(next()) == predicateResultToRemove) { - remove() - result = true - } - } - return result -} - -/** - * Removes all elements from this [MutableList] that match the given [predicate]. - * - * @return `true` if any element was removed from this collection, or `false` when no elements were removed and collection was not modified. - */ -public fun MutableList.removeAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, true) - -/** - * Retains only elements of this [MutableList] that match the given [predicate]. - * - * @return `true` if any element was removed from this collection, or `false` when all elements were retained and collection was not modified. - */ -public fun MutableList.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false) - -private fun MutableList.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean { - if (this !is RandomAccess) - return (this as MutableIterable).filterInPlace(predicate, predicateResultToRemove) - - var writeIndex: Int = 0 - for (readIndex in 0..lastIndex) { - val element = this[readIndex] - if (predicate(element) == predicateResultToRemove) - continue - - if (writeIndex != readIndex) - this[writeIndex] = element - - writeIndex++ - } - if (writeIndex < size) { - for (removeIndex in lastIndex downTo writeIndex) - removeAt(removeIndex) - - return true - } else { - return false - } -} - /** * Removes all elements from this [MutableCollection] that are also contained in the given [elements] collection. */ @@ -270,6 +197,117 @@ private fun MutableCollection<*>.retainNothing(): Boolean { return result } + +/** + * Removes all elements from this [MutableIterable] that match the given [predicate]. + * + * @return `true` if any element was removed from this collection, or `false` when no elements were removed and collection was not modified. + */ +public fun MutableIterable.removeAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, true) + +/** + * Retains only elements of this [MutableIterable] that match the given [predicate]. + * + * @return `true` if any element was removed from this collection, or `false` when all elements were retained and collection was not modified. + */ +public fun MutableIterable.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false) + +private fun MutableIterable.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean { + var result = false + with(iterator()) { + while (hasNext()) + if (predicate(next()) == predicateResultToRemove) { + remove() + result = true + } + } + return result +} + + +/** + * 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) } + + +/** + * Removes the element at the specified [index] from this list. + * In Kotlin one should use the [MutableList.removeAt] function instead. + */ +@Deprecated("Use removeAt(index) instead.", ReplaceWith("removeAt(index)"), level = DeprecationLevel.ERROR) +@kotlin.internal.InlineOnly +public inline fun MutableList.remove(index: Int): T = removeAt(index) + +/** + * Removes the first element from this mutable list and returns that removed element, or throws [NoSuchElementException] if this list is empty. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +public fun MutableList.removeFirst(): T = if (isEmpty()) throw NoSuchElementException("List is empty.") else removeAt(0) + +/** + * Removes the first element from this mutable list and returns that removed element, or returns `null` if this list is empty. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +public fun MutableList.removeFirstOrNull(): T? = if (isEmpty()) null else removeAt(0) + +/** + * Removes the last element from this mutable list and returns that removed element, or throws [NoSuchElementException] if this list is empty. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +public fun MutableList.removeLast(): T = if (isEmpty()) throw NoSuchElementException("List is empty.") else removeAt(lastIndex) + +/** + * Removes the last element from this mutable list and returns that removed element, or returns `null` if this list is empty. + */ +@SinceKotlin("1.3") +@ExperimentalStdlibApi +public fun MutableList.removeLastOrNull(): T? = if (isEmpty()) null else removeAt(lastIndex) + +/** + * Removes all elements from this [MutableList] that match the given [predicate]. + * + * @return `true` if any element was removed from this collection, or `false` when no elements were removed and collection was not modified. + */ +public fun MutableList.removeAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, true) + +/** + * Retains only elements of this [MutableList] that match the given [predicate]. + * + * @return `true` if any element was removed from this collection, or `false` when all elements were retained and collection was not modified. + */ +public fun MutableList.retainAll(predicate: (T) -> Boolean): Boolean = filterInPlace(predicate, false) + +private fun MutableList.filterInPlace(predicate: (T) -> Boolean, predicateResultToRemove: Boolean): Boolean { + if (this !is RandomAccess) + return (this as MutableIterable).filterInPlace(predicate, predicateResultToRemove) + + var writeIndex: Int = 0 + for (readIndex in 0..lastIndex) { + val element = this[readIndex] + if (predicate(element) == predicateResultToRemove) + continue + + if (writeIndex != readIndex) + this[writeIndex] = element + + writeIndex++ + } + if (writeIndex < size) { + for (removeIndex in lastIndex downTo writeIndex) + removeAt(removeIndex) + + return true + } else { + return false + } +} + /** * Randomly shuffles elements in this mutable list using the specified [random] instance as the source of randomness. * @@ -285,10 +323,3 @@ public fun MutableList.shuffle(random: Random): Unit { } } -/** - * 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) } - diff --git a/libraries/stdlib/test/collections/MutableCollectionsTest.kt b/libraries/stdlib/test/collections/MutableCollectionsTest.kt index 0bfd46ea513..b85600c0546 100644 --- a/libraries/stdlib/test/collections/MutableCollectionsTest.kt +++ b/libraries/stdlib/test/collections/MutableCollectionsTest.kt @@ -39,6 +39,26 @@ class MutableCollectionTest { } } + @Test fun removeFirst() { + val list = mutableListOf("first", "second") + + assertEquals("first", list.removeFirst()) + assertEquals("second", list.removeFirstOrNull()) + + assertNull(list.removeFirstOrNull()) + assertFailsWith { list.removeFirst() } + } + + @Test fun removeLast() { + val list = mutableListOf("first", "second") + + assertEquals("second", list.removeLast()) + assertEquals("first", list.removeLastOrNull()) + + assertNull(list.removeLastOrNull()) + assertFailsWith { list.removeLast() } + } + @Test fun removeAll() { val content = listOf("foo", "bar", "bar") val data = listOf("bar") diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 1c014b2bf66..6f86e72a3ca 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -2132,6 +2132,10 @@ public final class kotlin/collections/CollectionsKt { public static final fun removeAll (Ljava/util/Collection;Lkotlin/sequences/Sequence;)Z public static final fun removeAll (Ljava/util/Collection;[Ljava/lang/Object;)Z public static final fun removeAll (Ljava/util/List;Lkotlin/jvm/functions/Function1;)Z + public static final fun removeFirst (Ljava/util/List;)Ljava/lang/Object; + public static final fun removeFirstOrNull (Ljava/util/List;)Ljava/lang/Object; + public static final fun removeLast (Ljava/util/List;)Ljava/lang/Object; + public static final fun removeLastOrNull (Ljava/util/List;)Ljava/lang/Object; public static final fun requireNoNulls (Ljava/lang/Iterable;)Ljava/lang/Iterable; public static final fun requireNoNulls (Ljava/util/List;)Ljava/util/List; public static final fun retainAll (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Z