toHashSet is suboptimal for inputs with a lot of duplicates #KT-23142

This commit is contained in:
Abduqodiri Qurbonzoda
2020-04-15 02:20:50 +03:00
parent 2c4fcebfec
commit 0d5ec27ac4
6 changed files with 63 additions and 64 deletions
@@ -8686,7 +8686,7 @@ public inline fun <K, V, M : MutableMap<in K, in V>> CharArray.associateTo(desti
@SinceKotlin("1.4") @SinceKotlin("1.4")
@ExperimentalStdlibApi @ExperimentalStdlibApi
public inline fun <K, V> Array<out K>.associateWith(valueSelector: (K) -> V): Map<K, V> { public inline fun <K, V> Array<out K>.associateWith(valueSelector: (K) -> V): Map<K, V> {
val result = LinkedHashMap<K, V>() val result = LinkedHashMap<K, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8704,7 +8704,7 @@ public inline fun <K, V> Array<out K>.associateWith(valueSelector: (K) -> V): Ma
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> ByteArray.associateWith(valueSelector: (Byte) -> V): Map<Byte, V> { public inline fun <V> ByteArray.associateWith(valueSelector: (Byte) -> V): Map<Byte, V> {
val result = LinkedHashMap<Byte, V>() val result = LinkedHashMap<Byte, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8722,7 +8722,7 @@ public inline fun <V> ByteArray.associateWith(valueSelector: (Byte) -> V): Map<B
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> ShortArray.associateWith(valueSelector: (Short) -> V): Map<Short, V> { public inline fun <V> ShortArray.associateWith(valueSelector: (Short) -> V): Map<Short, V> {
val result = LinkedHashMap<Short, V>() val result = LinkedHashMap<Short, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8740,7 +8740,7 @@ public inline fun <V> ShortArray.associateWith(valueSelector: (Short) -> V): Map
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> IntArray.associateWith(valueSelector: (Int) -> V): Map<Int, V> { public inline fun <V> IntArray.associateWith(valueSelector: (Int) -> V): Map<Int, V> {
val result = LinkedHashMap<Int, V>() val result = LinkedHashMap<Int, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8758,7 +8758,7 @@ public inline fun <V> IntArray.associateWith(valueSelector: (Int) -> V): Map<Int
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> LongArray.associateWith(valueSelector: (Long) -> V): Map<Long, V> { public inline fun <V> LongArray.associateWith(valueSelector: (Long) -> V): Map<Long, V> {
val result = LinkedHashMap<Long, V>() val result = LinkedHashMap<Long, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8776,7 +8776,7 @@ public inline fun <V> LongArray.associateWith(valueSelector: (Long) -> V): Map<L
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> FloatArray.associateWith(valueSelector: (Float) -> V): Map<Float, V> { public inline fun <V> FloatArray.associateWith(valueSelector: (Float) -> V): Map<Float, V> {
val result = LinkedHashMap<Float, V>() val result = LinkedHashMap<Float, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8794,7 +8794,7 @@ public inline fun <V> FloatArray.associateWith(valueSelector: (Float) -> V): Map
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> DoubleArray.associateWith(valueSelector: (Double) -> V): Map<Double, V> { public inline fun <V> DoubleArray.associateWith(valueSelector: (Double) -> V): Map<Double, V> {
val result = LinkedHashMap<Double, V>() val result = LinkedHashMap<Double, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8812,7 +8812,7 @@ public inline fun <V> DoubleArray.associateWith(valueSelector: (Double) -> V): M
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> BooleanArray.associateWith(valueSelector: (Boolean) -> V): Map<Boolean, V> { public inline fun <V> BooleanArray.associateWith(valueSelector: (Boolean) -> V): Map<Boolean, V> {
val result = LinkedHashMap<Boolean, V>() val result = LinkedHashMap<Boolean, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -8830,7 +8830,7 @@ public inline fun <V> BooleanArray.associateWith(valueSelector: (Boolean) -> V):
@ExperimentalStdlibApi @ExperimentalStdlibApi
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> CharArray.associateWith(valueSelector: (Char) -> V): Map<Char, V> { public inline fun <V> CharArray.associateWith(valueSelector: (Char) -> V): Map<Char, V> {
val result = LinkedHashMap<Char, V>() val result = LinkedHashMap<Char, V>(mapCapacity(size.coerceAtMost(128)).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -9145,7 +9145,7 @@ public fun BooleanArray.toHashSet(): HashSet<Boolean> {
* Returns a new [HashSet] of all elements. * Returns a new [HashSet] of all elements.
*/ */
public fun CharArray.toHashSet(): HashSet<Char> { public fun CharArray.toHashSet(): HashSet<Char> {
return toCollection(HashSet<Char>(mapCapacity(size))) return toCollection(HashSet<Char>(mapCapacity(size.coerceAtMost(128))))
} }
/** /**
@@ -9439,7 +9439,7 @@ public fun CharArray.toSet(): Set<Char> {
return when (size) { return when (size) {
0 -> emptySet() 0 -> emptySet()
1 -> setOf(this[0]) 1 -> setOf(this[0])
else -> toCollection(LinkedHashSet<Char>(mapCapacity(size))) else -> toCollection(LinkedHashSet<Char>(mapCapacity(size.coerceAtMost(128))))
} }
} }
@@ -11168,9 +11168,7 @@ public infix fun CharArray.subtract(other: Iterable<Char>): Set<Char> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun <T> Array<out T>.toMutableSet(): MutableSet<T> { public fun <T> Array<out T>.toMutableSet(): MutableSet<T> {
val set = LinkedHashSet<T>(mapCapacity(size)) return toCollection(LinkedHashSet<T>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11179,9 +11177,7 @@ public fun <T> Array<out T>.toMutableSet(): MutableSet<T> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun ByteArray.toMutableSet(): MutableSet<Byte> { public fun ByteArray.toMutableSet(): MutableSet<Byte> {
val set = LinkedHashSet<Byte>(mapCapacity(size)) return toCollection(LinkedHashSet<Byte>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11190,9 +11186,7 @@ public fun ByteArray.toMutableSet(): MutableSet<Byte> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun ShortArray.toMutableSet(): MutableSet<Short> { public fun ShortArray.toMutableSet(): MutableSet<Short> {
val set = LinkedHashSet<Short>(mapCapacity(size)) return toCollection(LinkedHashSet<Short>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11201,9 +11195,7 @@ public fun ShortArray.toMutableSet(): MutableSet<Short> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun IntArray.toMutableSet(): MutableSet<Int> { public fun IntArray.toMutableSet(): MutableSet<Int> {
val set = LinkedHashSet<Int>(mapCapacity(size)) return toCollection(LinkedHashSet<Int>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11212,9 +11204,7 @@ public fun IntArray.toMutableSet(): MutableSet<Int> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun LongArray.toMutableSet(): MutableSet<Long> { public fun LongArray.toMutableSet(): MutableSet<Long> {
val set = LinkedHashSet<Long>(mapCapacity(size)) return toCollection(LinkedHashSet<Long>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11223,9 +11213,7 @@ public fun LongArray.toMutableSet(): MutableSet<Long> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun FloatArray.toMutableSet(): MutableSet<Float> { public fun FloatArray.toMutableSet(): MutableSet<Float> {
val set = LinkedHashSet<Float>(mapCapacity(size)) return toCollection(LinkedHashSet<Float>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11234,9 +11222,7 @@ public fun FloatArray.toMutableSet(): MutableSet<Float> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun DoubleArray.toMutableSet(): MutableSet<Double> { public fun DoubleArray.toMutableSet(): MutableSet<Double> {
val set = LinkedHashSet<Double>(mapCapacity(size)) return toCollection(LinkedHashSet<Double>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11245,9 +11231,7 @@ public fun DoubleArray.toMutableSet(): MutableSet<Double> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun BooleanArray.toMutableSet(): MutableSet<Boolean> { public fun BooleanArray.toMutableSet(): MutableSet<Boolean> {
val set = LinkedHashSet<Boolean>(mapCapacity(size)) return toCollection(LinkedHashSet<Boolean>(mapCapacity(size)))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -11256,9 +11240,7 @@ public fun BooleanArray.toMutableSet(): MutableSet<Boolean> {
* The returned set preserves the element iteration order of the original array. * The returned set preserves the element iteration order of the original array.
*/ */
public fun CharArray.toMutableSet(): MutableSet<Char> { public fun CharArray.toMutableSet(): MutableSet<Char> {
val set = LinkedHashSet<Char>(mapCapacity(size)) return toCollection(LinkedHashSet<Char>(mapCapacity(size.coerceAtMost(128))))
for (item in this) set.add(item)
return set
} }
/** /**
@@ -708,7 +708,7 @@ public inline fun <K, V, M : MutableMap<in K, in V>> CharSequence.associateTo(de
*/ */
@SinceKotlin("1.3") @SinceKotlin("1.3")
public inline fun <V> CharSequence.associateWith(valueSelector: (Char) -> V): Map<Char, V> { public inline fun <V> CharSequence.associateWith(valueSelector: (Char) -> V): Map<Char, V> {
val result = LinkedHashMap<Char, V>(mapCapacity(length).coerceAtLeast(16)) val result = LinkedHashMap<Char, V>(mapCapacity(length.coerceAtMost(128)).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -742,7 +742,7 @@ public fun <C : MutableCollection<in Char>> CharSequence.toCollection(destinatio
* Returns a new [HashSet] of all characters. * Returns a new [HashSet] of all characters.
*/ */
public fun CharSequence.toHashSet(): HashSet<Char> { public fun CharSequence.toHashSet(): HashSet<Char> {
return toCollection(HashSet<Char>(mapCapacity(length))) return toCollection(HashSet<Char>(mapCapacity(length.coerceAtMost(128))))
} }
/** /**
@@ -772,7 +772,7 @@ public fun CharSequence.toSet(): Set<Char> {
return when (length) { return when (length) {
0 -> emptySet() 0 -> emptySet()
1 -> setOf(this[0]) 1 -> setOf(this[0])
else -> toCollection(LinkedHashSet<Char>(mapCapacity(length))) else -> toCollection(LinkedHashSet<Char>(mapCapacity(length.coerceAtMost(128))))
} }
} }
@@ -4101,7 +4101,7 @@ public inline fun ShortArray.toUShortArray(): UShortArray {
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> UIntArray.associateWith(valueSelector: (UInt) -> V): Map<UInt, V> { public inline fun <V> UIntArray.associateWith(valueSelector: (UInt) -> V): Map<UInt, V> {
val result = LinkedHashMap<UInt, V>() val result = LinkedHashMap<UInt, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -4120,7 +4120,7 @@ public inline fun <V> UIntArray.associateWith(valueSelector: (UInt) -> V): Map<U
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> ULongArray.associateWith(valueSelector: (ULong) -> V): Map<ULong, V> { public inline fun <V> ULongArray.associateWith(valueSelector: (ULong) -> V): Map<ULong, V> {
val result = LinkedHashMap<ULong, V>() val result = LinkedHashMap<ULong, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -4139,7 +4139,7 @@ public inline fun <V> ULongArray.associateWith(valueSelector: (ULong) -> V): Map
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> UByteArray.associateWith(valueSelector: (UByte) -> V): Map<UByte, V> { public inline fun <V> UByteArray.associateWith(valueSelector: (UByte) -> V): Map<UByte, V> {
val result = LinkedHashMap<UByte, V>() val result = LinkedHashMap<UByte, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
@@ -4158,7 +4158,7 @@ public inline fun <V> UByteArray.associateWith(valueSelector: (UByte) -> V): Map
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <V> UShortArray.associateWith(valueSelector: (UShort) -> V): Map<UShort, V> { public inline fun <V> UShortArray.associateWith(valueSelector: (UShort) -> V): Map<UShort, V> {
val result = LinkedHashMap<UShort, V>() val result = LinkedHashMap<UShort, V>(mapCapacity(size).coerceAtLeast(16))
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
} }
+16
View File
@@ -8,6 +8,7 @@ package test.text
import kotlin.test.* import kotlin.test.*
import test.* import test.*
import test.collections.behaviors.iteratorBehavior import test.collections.behaviors.iteratorBehavior
import test.collections.behaviors.setBehavior
import test.collections.compare import test.collections.compare
import kotlin.math.sign import kotlin.math.sign
import kotlin.random.Random import kotlin.random.Random
@@ -1651,4 +1652,19 @@ ${" "}
assertFailsWith<IndexOutOfBoundsException> { "".elementAt(0) } assertFailsWith<IndexOutOfBoundsException> { "".elementAt(0) }
assertFailsWith<IndexOutOfBoundsException> { "a c".elementAt(-1) } assertFailsWith<IndexOutOfBoundsException> { "a c".elementAt(-1) }
} }
@Test
fun toHashSet() {
compare(hashSetOf('A', 'B', 'C'), "ACAABBAC".toHashSet()) { setBehavior() }
buildString {
repeat(100) { append('1') }
append('2')
repeat(100) { append('3') }
append('4')
repeat(100) { append('5') }
}.let {
compare(hashSetOf('1', '2', '3', '4', '5'), it.toHashSet()) { setBehavior() }
}
}
} }
@@ -5,10 +5,6 @@
package templates package templates
import templates.DocExtensions.collection
import templates.DocExtensions.element
import templates.DocExtensions.mapResult
import templates.DocExtensions.pluralize
import templates.Family.* import templates.Family.*
import templates.SequenceClass.* import templates.SequenceClass.*
@@ -35,11 +31,8 @@ object SetOps : TemplateGroupBase() {
""" """
} }
body(ArraysOfObjects, ArraysOfPrimitives) { body(ArraysOfObjects, ArraysOfPrimitives) {
""" val capacity = "size" + if (primitive == PrimitiveType.Char) ".coerceAtMost(128)" else ""
val set = LinkedHashSet<T>(mapCapacity(size)) "return toCollection(LinkedHashSet<T>(mapCapacity($capacity)))"
for (item in this) set.add(item)
return set
"""
} }
body(Sequences) { body(Sequences) {
""" """
@@ -55,7 +55,6 @@ object Snapshots : TemplateGroupBase() {
1 -> setOf(if (this is List) this[0] else iterator().next()) 1 -> setOf(if (this is List) this[0] else iterator().next())
else -> toCollection(LinkedHashSet<T>(mapCapacity(size))) else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
} }
} }
return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet() return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()
""" """
@@ -63,12 +62,13 @@ object Snapshots : TemplateGroupBase() {
body(Sequences) { "return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()" } body(Sequences) { "return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()" }
body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) { body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) {
val size = if (f == CharSequences) "length" else "size" val size = f.code.size
val capacity = if (f == CharSequences || primitive == PrimitiveType.Char) "$size.coerceAtMost(128)" else size
""" """
return when ($size) { return when ($size) {
0 -> emptySet() 0 -> emptySet()
1 -> setOf(this[0]) 1 -> setOf(this[0])
else -> toCollection(LinkedHashSet<T>(mapCapacity($size))) else -> toCollection(LinkedHashSet<T>(mapCapacity($capacity)))
} }
""" """
} }
@@ -82,8 +82,11 @@ object Snapshots : TemplateGroupBase() {
returns("HashSet<T>") returns("HashSet<T>")
body { "return toCollection(HashSet<T>(mapCapacity(collectionSizeOrDefault(12))))" } body { "return toCollection(HashSet<T>(mapCapacity(collectionSizeOrDefault(12))))" }
body(Sequences) { "return toCollection(HashSet<T>())" } body(Sequences) { "return toCollection(HashSet<T>())" }
body(CharSequences) { "return toCollection(HashSet<T>(mapCapacity(length)))" } body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) {
body(ArraysOfObjects, ArraysOfPrimitives) { "return toCollection(HashSet<T>(mapCapacity(size)))" } val size = f.code.size
val capacity = if (f == CharSequences || primitive == PrimitiveType.Char) "$size.coerceAtMost(128)" else size
"return toCollection(HashSet<T>(mapCapacity($capacity)))"
}
} }
val f_toSortedSet = fn("toSortedSet()") { val f_toSortedSet = fn("toSortedSet()") {
@@ -467,13 +470,18 @@ object Snapshots : TemplateGroupBase() {
else -> "samples.collections.Collections.Transformations.associateWith" else -> "samples.collections.Collections.Transformations.associateWith"
}) })
body { body {
val resultMap = when (family) { val capacity = when (family) {
Iterables -> "LinkedHashMap<K, V>(mapCapacity(collectionSizeOrDefault(10)).coerceAtLeast(16))" Iterables -> "mapCapacity(collectionSizeOrDefault(10)).coerceAtLeast(16)"
CharSequences -> "LinkedHashMap<K, V>(mapCapacity(length).coerceAtLeast(16))" CharSequences -> "mapCapacity(length.coerceAtMost(128)).coerceAtLeast(16)"
else -> "LinkedHashMap<K, V>()" ArraysOfObjects, ArraysOfPrimitives, ArraysOfUnsigned -> if (primitive == PrimitiveType.Char) {
"mapCapacity(size.coerceAtMost(128)).coerceAtLeast(16)"
} else {
"mapCapacity(size).coerceAtLeast(16)"
}
else -> ""
} }
""" """
val result = $resultMap val result = LinkedHashMap<K, V>($capacity)
return associateWithTo(result, valueSelector) return associateWithTo(result, valueSelector)
""" """
} }