diff --git a/libraries/stdlib/src/generated/_Generators.kt b/libraries/stdlib/src/generated/_Generators.kt index 669273ccc14..0101a25914b 100644 --- a/libraries/stdlib/src/generated/_Generators.kt +++ b/libraries/stdlib/src/generated/_Generators.kt @@ -381,6 +381,140 @@ public fun Sequence.merge(sequence: Sequence, transform: (T, R) return MergingSequence(this, sequence, transform) } +/** + * Returns a list containing all elements of the original collection except the elements contained in the given [array]. + */ +public fun Iterable.minus(array: Array): List { + if (array.isEmpty()) return this.toList() + val other = array.toHashSet() + return this.filterNot { it in other } +} + +/** + * Returns a sequence containing all elements of original sequence except the elements contained in the given [array]. + */ +public fun Sequence.minus(array: Array): Sequence { + if (array.isEmpty()) return this + return object: Sequence { + override fun iterator(): Iterator { + val other = array.toHashSet() + return this@minus.filterNot { it in other }.iterator() + } + } +} + +/** + * Returns a set containing all elements of the original set except the elements contained in the given [array]. + */ +public fun Set.minus(array: Array): Set { + val result = LinkedHashSet(this) + result.removeAll(array) + return result +} + +/** + * Returns a list containing all elements of the original collection except the elements contained in the given [collection]. + */ +public fun Iterable.minus(collection: Iterable): List { + val other = collection.convertToSetForSetOperationWith(this) + if (other.isEmpty()) + return this.toList() + return this.filterNot { it in other } +} + +/** + * Returns a sequence containing all elements of original sequence except the elements contained in the given [collection]. + */ +public fun Sequence.minus(collection: Iterable): Sequence { + return object: Sequence { + override fun iterator(): Iterator { + val other = collection.convertToSetForSetOperation() + if (other.isEmpty()) + return this@minus.iterator() + else + return this@minus.filterNot { it in other }.iterator() + } + } +} + +/** + * Returns a set containing all elements of the original set except the elements contained in the given [collection]. + */ +public fun Set.minus(collection: Iterable): Set { + val other = collection.convertToSetForSetOperationWith(this) + if (other.isEmpty()) + return this.toSet() + if (other is Set) + return this.filterNotTo(LinkedHashSet()) { it in other } + val result = LinkedHashSet(this) + result.removeAll(other) + return result +} + +/** + * Returns a list containing all elements of the original collection without the first occurrence of the given [element]. + */ +public fun Iterable.minus(element: T): List { + val result = ArrayList(collectionSizeOrDefault(10)) + var removed = false + return this.filterTo(result) { if (!removed && it == element) { removed = true; false } else true } +} + +/** + * Returns a sequence containing all elements of the original sequence without the first occurrence of the given [element]. + */ +public fun Sequence.minus(element: T): Sequence { + return object: Sequence { + override fun iterator(): Iterator { + var removed = false + return this@minus.filter { if (!removed && it == element) { removed = true; false } else true }.iterator() + } + } +} + +/** + * Returns a set containing all elements of the original set except the given [element]. + */ +public fun Set.minus(element: T): Set { + val result = LinkedHashSet(mapCapacity(size())) + var removed = false + return this.filterTo(result) { if (!removed && it == element) { removed = true; false } else true } +} + +/** + * Returns a list containing all elements of the original collection except the elements contained in the given [sequence]. + */ +public fun Iterable.minus(sequence: Sequence): List { + val other = sequence.toHashSet() + if (other.isEmpty()) + return this.toList() + return this.filterNot { it in other } +} + +/** + * Returns a sequence containing all elements of original sequence except the elements contained in the given [sequence]. + */ +public fun Sequence.minus(sequence: Sequence): Sequence { + return object: Sequence { + override fun iterator(): Iterator { + val other = sequence.toHashSet() + if (other.isEmpty()) + return this@minus.iterator() + else + return this@minus.filterNot { it in other }.iterator() + } + } +} + +/** + * Returns a set containing all elements of the original set except the elements contained in the given [sequence]. + */ +public fun Set.minus(sequence: Sequence): Set { + val result = LinkedHashSet(this) + result.removeAll(sequence) + return result +} + /** * Splits original collection into pair of collections, * where *first* collection contains elements for which [predicate] yielded `true`, diff --git a/libraries/stdlib/src/kotlin/collections/JUtil.kt b/libraries/stdlib/src/kotlin/collections/JUtil.kt index 0b3971f7428..44c1aa0cd84 100644 --- a/libraries/stdlib/src/kotlin/collections/JUtil.kt +++ b/libraries/stdlib/src/kotlin/collections/JUtil.kt @@ -126,4 +126,27 @@ public fun Iterable.collectionSizeOrNull(): Int? = if (this is Collection /** * Returns the size of this iterable if it is known, or the specified [default] value otherwise. */ -public fun Iterable.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) size() else default \ No newline at end of file +public fun Iterable.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) size() else default + +/** Returns true when it's safe to convert this collection to a set without changing contains method behavior. */ +private fun Collection.safeToConvertToSet() = size() > 2 && this is ArrayList + +/** Converts this collection to a set, when it's worth so and it doesn't change contains method behavior. */ +private fun Iterable.convertToSetForSetOperationWith(source: Iterable): Collection = + when(this) { + is Set -> this + is Collection -> + when { + source is Collection && source.size() < 2 -> this + else -> if (this.safeToConvertToSet()) toHashSet() else this + } + else -> toHashSet() + } + +/** Converts this collection to a set, when it's worth so and it doesn't change contains method behavior. */ +private fun Iterable.convertToSetForSetOperation(): Collection = + when(this) { + is Set -> this + is Collection -> if (this.safeToConvertToSet()) toHashSet() else this + else -> toHashSet() + } diff --git a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt index e88d6dd5eb5..8c5006046f5 100644 --- a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt +++ b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt @@ -28,6 +28,34 @@ public fun MutableCollection.plusAssign(sequence: Sequence) { this.addAll(sequence) } +/** + * Removes a single instance of the specified [element] from this mutable collection. + */ +public fun MutableCollection.minusAssign(element: T) { + this.remove(element) +} + +/** + * Removes all elements contained in the given [collection] from this mutable collection. + */ +public fun MutableCollection.minusAssign(collection: Iterable) { + this.removeAll(collection) +} + +/** + * Removes all elements contained in the given [array] from this mutable collection. + */ +public fun MutableCollection.minusAssign(array: Array) { + this.removeAll(array) +} + +/** + * Removes all elements contained in the given [sequence] from this mutable collection. + */ +public fun MutableCollection.minusAssign(sequence: Sequence) { + this.removeAll(sequence) +} + /** * Adds all elements of the given [iterable] to this [MutableCollection]. */ @@ -56,46 +84,53 @@ public fun MutableCollection.addAll(array: Array) { * Removes all elements from this [MutableCollection] that are also contained in the given [iterable]. */ public fun MutableCollection.removeAll(iterable: Iterable) { - when (iterable) { - is Collection -> removeAll(iterable) - else -> removeAll(iterable.toHashSet()) - } + removeAll(iterable.convertToSetForSetOperationWith(this)) } /** * Removes all elements from this [MutableCollection] that are also contained in the given [sequence]. */ public fun MutableCollection.removeAll(sequence: Sequence) { - removeAll(sequence.toHashSet()) + val set = sequence.toHashSet() + if (set.isNotEmpty()) + removeAll(set) } /** * Removes all elements from this [MutableCollection] that are also contained in the given [array]. */ public fun MutableCollection.removeAll(array: Array) { - removeAll(array.toHashSet()) + if (array.isNotEmpty()) + removeAll(array.toHashSet()) +// else +// removeAll(emptyList()) } /** * Retains only elements of this [MutableCollection] that are contained in the given [iterable]. */ public fun MutableCollection.retainAll(iterable: Iterable) { - when (iterable) { - is Collection -> retainAll(iterable) - else -> retainAll(iterable.toHashSet()) - } + retainAll(iterable.convertToSetForSetOperationWith(this)) } /** * Retains only elements of this [MutableCollection] that are contained in the given [array]. */ public fun MutableCollection.retainAll(array: Array) { - retainAll(array.toHashSet()) + if (array.isNotEmpty()) + retainAll(array.toHashSet()) + else + clear() +// retainAll(emptyList()) } /** * Retains only elements of this [MutableCollection] that are contained in the given [sequence]. */ public fun MutableCollection.retainAll(sequence: Sequence) { - retainAll(sequence.toHashSet()) + val set = sequence.toHashSet() + if (set.isNotEmpty()) + retainAll(set) + else + clear() } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt index c312f36d1f0..e5cab227837 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Generators.kt @@ -177,6 +177,163 @@ fun generators(): List { } } + templates add f("minus(element: T)") { + only(Iterables, Sets, Sequences) + doc { "Returns a list containing all elements of the original collection without the first occurrence of the given [element]." } + returns("List") + body { + """ + val result = ArrayList(collectionSizeOrDefault(10)) + var removed = false + return this.filterTo(result) { if (!removed && it == element) { removed = true; false } else true } + """ + } + + returns("SELF", Sets, Sequences) + doc(Sets) { "Returns a set containing all elements of the original set except the given [element]." } + body(Sets) { + """ + val result = LinkedHashSet(mapCapacity(size())) + var removed = false + return this.filterTo(result) { if (!removed && it == element) { removed = true; false } else true } + """ + } + + + doc(Sequences) { "Returns a sequence containing all elements of the original sequence without the first occurrence of the given [element]." } + body(Sequences) { + """ + return object: Sequence { + override fun iterator(): Iterator { + var removed = false + return this@minus.filter { if (!removed && it == element) { removed = true; false } else true }.iterator() + } + } + """ + } + } + + + templates add f("minus(collection: Iterable)") { + only(Iterables, Sets, Sequences) + doc { "Returns a list containing all elements of the original collection except the elements contained in the given [collection]." } + returns("List") + returns("SELF", Sets, Sequences) + body { + """ + val other = collection.convertToSetForSetOperationWith(this) + if (other.isEmpty()) + return this.toList() + + return this.filterNot { it in other } + """ + } + + doc(Sets) { "Returns a set containing all elements of the original set except the elements contained in the given [collection]." } + body(Sets) { + """ + val other = collection.convertToSetForSetOperationWith(this) + if (other.isEmpty()) + return this.toSet() + if (other is Set) + return this.filterNotTo(LinkedHashSet()) { it in other } + + val result = LinkedHashSet(this) + result.removeAll(other) + return result + """ + } + + doc(Sequences) { "Returns a sequence containing all elements of original sequence except the elements contained in the given [collection]." } + body(Sequences) { + """ + return object: Sequence { + override fun iterator(): Iterator { + val other = collection.convertToSetForSetOperation() + if (other.isEmpty()) + return this@minus.iterator() + else + return this@minus.filterNot { it in other }.iterator() + } + } + """ + } + } + + templates add f("minus(array: Array)") { + only(Iterables, Sets, Sequences) + doc { "Returns a list containing all elements of the original collection except the elements contained in the given [array]." } + returns("List") + returns("SELF", Sets, Sequences) + body { + """ + if (array.isEmpty()) return this.toList() + val other = array.toHashSet() + return this.filterNot { it in other } + """ + } + doc(Sets) { "Returns a set containing all elements of the original set except the elements contained in the given [array]." } + body(Sets) { + """ + val result = LinkedHashSet(this) + result.removeAll(array) + return result + """ + } + + doc(Sequences) { "Returns a sequence containing all elements of original sequence except the elements contained in the given [array]." } + body(Sequences) { + """ + if (array.isEmpty()) return this + return object: Sequence { + override fun iterator(): Iterator { + val other = array.toHashSet() + return this@minus.filterNot { it in other }.iterator() + } + } + """ + } + } + + templates add f("minus(sequence: Sequence)") { + only(Iterables, Sets) + doc { "Returns a list containing all elements of the original collection except the elements contained in the given [sequence]." } + returns("List") + returns("SELF", Sets, Sequences) + body { + """ + val other = sequence.toHashSet() + if (other.isEmpty()) + return this.toList() + + return this.filterNot { it in other } + """ + } + doc(Sets) { "Returns a set containing all elements of the original set except the elements contained in the given [sequence]." } + body(Sets) { + """ + val result = LinkedHashSet(this) + result.removeAll(sequence) + return result + """ + } + + doc(Sequences) { "Returns a sequence containing all elements of original sequence except the elements contained in the given [sequence]." } + body(Sequences) { + """ + return object: Sequence { + override fun iterator(): Iterator { + val other = sequence.toHashSet() + if (other.isEmpty()) + return this@minus.iterator() + else + return this@minus.filterNot { it in other }.iterator() + } + } + """ + } + } + templates add f("partition(predicate: (T) -> Boolean)") { inline(true)