KT-55091 Optimize Sequence::toSet/toList implementation
Sequence::toSet and Sequence::toList both create a collection, fill it with elements, and then try to optimize a result by returning empty, singleton, or the allocated collection depending on the elements count. Instead of allocating the collection and then trying to return an optimized instance it is worth checking the sequence's size beforehand and return empty/singleton collection when possible. Proposed change optimize performance of aforementioned functions and also reduce allocation rate when a sequence consists of 0 or 1 elements. ^KT-55091 fixed
This commit is contained in:
committed by
Space Team
parent
e8be3043cc
commit
368f21461c
@@ -805,7 +805,16 @@ public fun <T> Sequence<T>.toHashSet(): HashSet<T> {
|
||||
* The operation is _terminal_.
|
||||
*/
|
||||
public fun <T> Sequence<T>.toList(): List<T> {
|
||||
return this.toMutableList().optimizeReadOnlyList()
|
||||
val it = iterator()
|
||||
if (!it.hasNext())
|
||||
return emptyList()
|
||||
val element = it.next()
|
||||
if (!it.hasNext())
|
||||
return listOf(element)
|
||||
val dst = ArrayList<T>()
|
||||
dst.add(element)
|
||||
while (it.hasNext()) dst.add(it.next())
|
||||
return dst
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -825,7 +834,16 @@ public fun <T> Sequence<T>.toMutableList(): MutableList<T> {
|
||||
* The operation is _terminal_.
|
||||
*/
|
||||
public fun <T> Sequence<T>.toSet(): Set<T> {
|
||||
return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()
|
||||
val it = iterator()
|
||||
if (!it.hasNext())
|
||||
return emptySet()
|
||||
val element = it.next()
|
||||
if (!it.hasNext())
|
||||
return setOf(element)
|
||||
val dst = LinkedHashSet<T>()
|
||||
dst.add(element)
|
||||
while (it.hasNext()) dst.add(it.next())
|
||||
return dst
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -745,6 +745,20 @@ public class SequenceTest {
|
||||
assertNull(fibonacci().take(10).firstNotNullOfOrNull { it.doubleIfNotMonodigit() })
|
||||
}
|
||||
|
||||
@Test fun toSet() {
|
||||
assertEquals(emptySet(), emptySequence<Int>().toSet())
|
||||
assertEquals(setOf(42), sequenceOf(42).toSet())
|
||||
assertEquals(setOf(3, 2, 1), sequenceOf(3, 2, 1).toSet())
|
||||
assertEquals(setOf(1, 2, 3), sequenceOf(1, 2, 1, 3, 2, 3).toSet())
|
||||
}
|
||||
|
||||
@Test fun toList() {
|
||||
assertEquals(emptyList(), emptySequence<Int>().toList())
|
||||
assertEquals(listOf(42), sequenceOf(42).toList())
|
||||
assertEquals(listOf(3, 2, 1), sequenceOf(3, 2, 1).toList())
|
||||
assertEquals(listOf(1, 2, 1, 3, 2, 3), sequenceOf(1, 2, 1, 3, 2, 3).toList())
|
||||
}
|
||||
|
||||
/*
|
||||
test fun pairIterator() {
|
||||
val pairStr = (fibonacci() zip fibonacci().map { i -> i*2 }).joinToString(limit = 10)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -35,6 +35,20 @@ object Snapshots : TemplateGroupBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun optimizedSequenceToCollection(emptyFactory: String, singleElementFactory: String, dstType: String) =
|
||||
"""
|
||||
val it = iterator()
|
||||
if (!it.hasNext())
|
||||
return $emptyFactory()
|
||||
val element = it.next()
|
||||
if (!it.hasNext())
|
||||
return $singleElementFactory(element)
|
||||
val dst = $dstType<T>()
|
||||
dst.add(element)
|
||||
while (it.hasNext()) dst.add(it.next())
|
||||
return dst
|
||||
"""
|
||||
|
||||
val f_toSet = fn("toSet()") {
|
||||
includeDefault()
|
||||
include(CharSequences)
|
||||
@@ -59,8 +73,7 @@ object Snapshots : TemplateGroupBase() {
|
||||
return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()
|
||||
"""
|
||||
}
|
||||
body(Sequences) { "return toCollection(LinkedHashSet<T>()).optimizeReadOnlySet()" }
|
||||
|
||||
body(Sequences) { optimizedSequenceToCollection("emptySet", "setOf", "LinkedHashSet") }
|
||||
body(CharSequences, ArraysOfObjects, ArraysOfPrimitives) {
|
||||
val size = f.code.size
|
||||
val capacity = if (f == CharSequences || primitive == PrimitiveType.Char) "$size.coerceAtMost(128)" else size
|
||||
@@ -169,6 +182,7 @@ object Snapshots : TemplateGroupBase() {
|
||||
}
|
||||
"""
|
||||
}
|
||||
body(Sequences) { optimizedSequenceToCollection("emptyList", "listOf", "ArrayList") }
|
||||
specialFor(Maps) {
|
||||
doc { "Returns a [List] containing all key-value pairs." }
|
||||
returns("List<Pair<K, V>>")
|
||||
|
||||
Reference in New Issue
Block a user