diff --git a/js/js.libraries/src/core/kotlin.kt b/js/js.libraries/src/core/kotlin.kt index 947b10f99ab..e2e9f293918 100644 --- a/js/js.libraries/src/core/kotlin.kt +++ b/js/js.libraries/src/core/kotlin.kt @@ -91,6 +91,8 @@ internal fun arrayPlusCollection(array: dynamic, collection: Collection): return result } +// no singleton map implementation in js, return map as is +internal inline fun Map.toSingletonMap(): Map = this internal inline fun Array.copyToArrayOfAny(isVarargs: Boolean): Array = if (isVarargs) diff --git a/libraries/stdlib/src/generated/_Arrays.kt b/libraries/stdlib/src/generated/_Arrays.kt index f00fda05d51..246b52fefd3 100644 --- a/libraries/stdlib/src/generated/_Arrays.kt +++ b/libraries/stdlib/src/generated/_Arrays.kt @@ -6308,63 +6308,99 @@ public fun CharArray.toHashSet(): HashSet { * Returns a [List] containing all elements. */ public fun Array.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun ByteArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun ShortArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun IntArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun LongArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun FloatArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun DoubleArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun BooleanArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** * Returns a [List] containing all elements. */ public fun CharArray.toList(): List { - return this.toMutableList() + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** @@ -6450,63 +6486,99 @@ public fun CharArray.toMutableList(): MutableList { * Returns a [Set] of all elements. */ public fun Array.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun ByteArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun ShortArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun IntArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun LongArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun FloatArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun DoubleArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun BooleanArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** * Returns a [Set] of all elements. */ public fun CharArray.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(size))) + return when (size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } } /** diff --git a/libraries/stdlib/src/generated/_Collections.kt b/libraries/stdlib/src/generated/_Collections.kt index 3267c6211f1..e0348dfa6b7 100644 --- a/libraries/stdlib/src/generated/_Collections.kt +++ b/libraries/stdlib/src/generated/_Collections.kt @@ -1003,7 +1003,14 @@ public fun Iterable.toHashSet(): HashSet { * Returns a [List] containing all elements. */ public fun Iterable.toList(): List { - return this.toMutableList() + if (this is Collection) { + return when (size) { + 0 -> emptyList() + 1 -> listOf(if (this is List) get(0) else iterator().next()) + else -> this.toMutableList() + } + } + return this.toMutableList().optimizeReadOnlyList() } /** @@ -1026,7 +1033,14 @@ public fun Collection.toMutableList(): MutableList { * Returns a [Set] of all elements. */ public fun Iterable.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(collectionSizeOrDefault(12)))) + if (this is Collection) { + return when (size) { + 0 -> emptySet() + 1 -> setOf(if (this is List) this[0] else iterator().next()) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } + } + return toCollection(LinkedHashSet()).optimizeReadOnlySet() } /** diff --git a/libraries/stdlib/src/generated/_Maps.kt b/libraries/stdlib/src/generated/_Maps.kt index 37a2b217197..03eac7565f3 100644 --- a/libraries/stdlib/src/generated/_Maps.kt +++ b/libraries/stdlib/src/generated/_Maps.kt @@ -17,9 +17,19 @@ import java.util.Collections // TODO: it's temporary while we have java.util.Col * Returns a [List] containing all key-value pairs. */ public fun Map.toList(): List> { + if (size == 0) + return emptyList() + val iterator = entries.iterator() + if (!iterator.hasNext()) + return emptyList() + val first = iterator.next() + if (!iterator.hasNext()) + return listOf(first.toPair()) val result = ArrayList>(size) - for (item in this) - result.add(item.key to item.value) + result.add(first.toPair()) + do { + result.add(iterator.next().toPair()) + } while (iterator.hasNext()) return result } diff --git a/libraries/stdlib/src/generated/_Sequences.kt b/libraries/stdlib/src/generated/_Sequences.kt index 1fc51c0c1f2..b39b300bee3 100644 --- a/libraries/stdlib/src/generated/_Sequences.kt +++ b/libraries/stdlib/src/generated/_Sequences.kt @@ -522,7 +522,7 @@ public fun Sequence.toHashSet(): HashSet { * Returns a [List] containing all elements. */ public fun Sequence.toList(): List { - return this.toMutableList() + return this.toMutableList().optimizeReadOnlyList() } /** @@ -536,7 +536,7 @@ public fun Sequence.toMutableList(): MutableList { * Returns a [Set] of all elements. */ public fun Sequence.toSet(): Set { - return toCollection(LinkedHashSet()) + return toCollection(LinkedHashSet()).optimizeReadOnlySet() } /** diff --git a/libraries/stdlib/src/generated/_Strings.kt b/libraries/stdlib/src/generated/_Strings.kt index cb66cad73a4..f93d0560d46 100644 --- a/libraries/stdlib/src/generated/_Strings.kt +++ b/libraries/stdlib/src/generated/_Strings.kt @@ -590,7 +590,11 @@ public fun CharSequence.toHashSet(): HashSet { * Returns a [List] containing all characters. */ public fun CharSequence.toList(): List { - return this.toMutableList() + return when (length) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } } /** @@ -604,7 +608,11 @@ public fun CharSequence.toMutableList(): MutableList { * Returns a [Set] of all characters. */ public fun CharSequence.toSet(): Set { - return toCollection(LinkedHashSet(mapCapacity(length))) + return when (length) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity(length))) + } } /** diff --git a/libraries/stdlib/src/kotlin/collections/Collections.kt b/libraries/stdlib/src/kotlin/collections/Collections.kt index 6b8d3c43b2d..c62a7639a48 100644 --- a/libraries/stdlib/src/kotlin/collections/Collections.kt +++ b/libraries/stdlib/src/kotlin/collections/Collections.kt @@ -130,6 +130,11 @@ public inline fun Enumeration.toList(): List = Collections.list(this) @kotlin.internal.InlineOnly public inline fun <@kotlin.internal.OnlyInputTypes T> Collection.containsAll(elements: Collection): Boolean = this.containsAll(elements) +internal fun List.optimizeReadOnlyList() = when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this +} // copies typed varargs array to array of objects @JvmVersion diff --git a/libraries/stdlib/src/kotlin/collections/Maps.kt b/libraries/stdlib/src/kotlin/collections/Maps.kt index 445bd9d6155..cc370eae711 100644 --- a/libraries/stdlib/src/kotlin/collections/Maps.kt +++ b/libraries/stdlib/src/kotlin/collections/Maps.kt @@ -283,7 +283,7 @@ public fun MutableMap.putAll(pairs: Sequence>): Uni */ @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") public inline fun Map.mapValues(transform: (Map.Entry) -> R): Map { - return mapValuesTo(LinkedHashMap(mapCapacity(size)), transform) + return mapValuesTo(LinkedHashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap() } /** @@ -294,7 +294,7 @@ public inline fun Map.mapValues(transform: (Map.Entry) -> */ @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") public inline fun Map.mapKeys(transform: (Map.Entry) -> R): Map { - return mapKeysTo(LinkedHashMap(mapCapacity(size)), transform) + return mapKeysTo(LinkedHashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap() } /** @@ -369,7 +369,16 @@ public inline fun Map.filterNot(predicate: (Map.Entry) -> Boo /** * Returns a new map containing all key-value pairs from the given collection of pairs. */ -public fun Iterable>.toMap(): Map = toMap(LinkedHashMap(collectionSizeOrNull()?.let { mapCapacity(it) } ?: 16)) +public fun Iterable>.toMap(): Map { + if (this is Collection) { + return when (size) { + 0 -> emptyMap() + 1 -> mapOf(if (this is List) this[0] else iterator().next()) + else -> toMap(LinkedHashMap(mapCapacity(size))) + } + } + return toMap(LinkedHashMap()).optimizeReadOnlyMap() +} /** * Populates and returns the [destination] mutable map with key-value pairs from the given collection of pairs. @@ -380,7 +389,11 @@ public fun > Iterable>.toMap(destina /** * Returns a new map containing all key-value pairs from the given array of pairs. */ -public fun Array>.toMap(): Map = toMap(LinkedHashMap(mapCapacity(size))) +public fun Array>.toMap(): Map = when(size) { + 0 -> emptyMap() + 1 -> mapOf(this[0]) + else -> toMap(LinkedHashMap(mapCapacity(size))) +} /** * Populates and returns the [destination] mutable map with key-value pairs from the given array of pairs. @@ -392,7 +405,7 @@ public fun > Array>.toMap(destin * Returns a new map containing all key-value pairs from the given sequence of pairs. */ -public fun Sequence>.toMap(): Map = toMap(LinkedHashMap()) +public fun Sequence>.toMap(): Map = toMap(LinkedHashMap()).optimizeReadOnlyMap() /** * Populates and returns the [destination] mutable map with key-value pairs from the given sequence of pairs. @@ -405,25 +418,25 @@ public fun > Sequence>.toMap(destina * Creates a new read-only map by replacing or adding an entry to this map from a given key-value [pair]. */ public operator fun Map.plus(pair: Pair): Map - = LinkedHashMap(this).apply { put(pair.first, pair.second) } + = if (this.isEmpty()) mapOf(pair) else LinkedHashMap(this).apply { put(pair.first, pair.second) } /** * Creates a new read-only map by replacing or adding entries to this map from a given collection of key-value [pairs]. */ public operator fun Map.plus(pairs: Iterable>): Map - = LinkedHashMap(this).apply { putAll(pairs) } + = if (this.isEmpty()) pairs.toMap() else LinkedHashMap(this).apply { putAll(pairs) } /** * Creates a new read-only map by replacing or adding entries to this map from a given array of key-value [pairs]. */ public operator fun Map.plus(pairs: Array>): Map - = LinkedHashMap(this).apply { putAll(pairs) } + = if (this.isEmpty()) pairs.toMap() else LinkedHashMap(this).apply { putAll(pairs) } /** * Creates a new read-only map by replacing or adding entries to this map from a given sequence of key-value [pairs]. */ public operator fun Map.plus(pairs: Sequence>): Map - = LinkedHashMap(this).apply { putAll(pairs) } + = LinkedHashMap(this).apply { putAll(pairs) }.optimizeReadOnlyMap() /** * Creates a new read-only map by replacing or adding entries to this map from another [map]. @@ -471,3 +484,15 @@ public inline operator fun MutableMap.plusAssign(pairs: Seque public inline operator fun MutableMap.plusAssign(map: Map) { putAll(map) } + + +// do not expose for now @kotlin.internal.InlineExposed +internal fun Map.optimizeReadOnlyMap() = when (size) { + 0 -> emptyMap() + 1 -> toSingletonMap() + else -> this +} + +@kotlin.jvm.JvmVersion +internal fun Map.toSingletonMap(): Map + = with (entries.iterator().next()) { Collections.singletonMap(key, value) } diff --git a/libraries/stdlib/src/kotlin/collections/Sets.kt b/libraries/stdlib/src/kotlin/collections/Sets.kt index 46815ea5c9e..a8f6d62e224 100644 --- a/libraries/stdlib/src/kotlin/collections/Sets.kt +++ b/libraries/stdlib/src/kotlin/collections/Sets.kt @@ -64,3 +64,10 @@ public fun sortedSetOf(vararg elements: T): TreeSet = elements.toCollecti */ @JvmVersion public fun sortedSetOf(comparator: Comparator, vararg elements: T): TreeSet = elements.toCollection(TreeSet(comparator)) + + +internal fun Set.optimizeReadOnlySet() = when (size) { + 0 -> emptySet() + 1 -> setOf(iterator().next()) + else -> this +} \ No newline at end of file diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Snapshots.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Snapshots.kt index a11907cb7e1..8c0002e00b3 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Snapshots.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Snapshots.kt @@ -23,10 +23,31 @@ fun snapshots(): List { templates add f("toSet()") { doc { f -> "Returns a [Set] of all ${f.element.pluralize()}." } returns("Set") - body { "return toCollection(LinkedHashSet(mapCapacity(collectionSizeOrDefault(12))))" } - body(Sequences) { "return toCollection(LinkedHashSet())" } - body(CharSequences) { "return toCollection(LinkedHashSet(mapCapacity(length)))" } - body(ArraysOfObjects, ArraysOfPrimitives) { "return toCollection(LinkedHashSet(mapCapacity(size)))" } + body(Iterables) { + """ + if (this is Collection) { + return when (size) { + 0 -> emptySet() + 1 -> setOf(if (this is List) this[0] else iterator().next()) + else -> toCollection(LinkedHashSet(mapCapacity(size))) + } + + } + return toCollection(LinkedHashSet()).optimizeReadOnlySet() + """ + } + body(Sequences) { "return toCollection(LinkedHashSet()).optimizeReadOnlySet()" } + + body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) { f -> + val size = if (f == CharSequences) "length" else "size" + """ + return when ($size) { + 0 -> emptySet() + 1 -> setOf(this[0]) + else -> toCollection(LinkedHashSet(mapCapacity($size))) + } + """ + } } templates add f("toHashSet()") { @@ -90,9 +111,19 @@ fun snapshots(): List { returns("List>") body { """ + if (size == 0) + return emptyList() + val iterator = entries.iterator() + if (!iterator.hasNext()) + return emptyList() + val first = iterator.next() + if (!iterator.hasNext()) + return listOf(first.toPair()) val result = ArrayList>(size) - for (item in this) - result.add(item.key to item.value) + result.add(first.toPair()) + do { + result.add(iterator.next().toPair()) + } while (iterator.hasNext()) return result """ } @@ -102,7 +133,28 @@ fun snapshots(): List { include(CharSequences) doc { f -> "Returns a [List] containing all ${f.element.pluralize()}." } returns("List") - body { "return this.toMutableList()" } + body { "return this.toMutableList().optimizeReadOnlyList()" } + body(Iterables) { + """ + if (this is Collection) { + return when (size) { + 0 -> emptyList() + 1 -> listOf(if (this is List) get(0) else iterator().next()) + else -> this.toMutableList() + } + } + return this.toMutableList().optimizeReadOnlyList() + """ + } + body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { f -> + """ + return when (${ if (f == CharSequences) "length" else "size" }) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } + """ + } } templates add f("associate(transform: (T) -> Pair)") {