From c5c1ecf6c0cf86ffa28a6d6ff0b2117c9c9ecd35 Mon Sep 17 00:00:00 2001 From: Ilya Ryzhenkov Date: Mon, 1 Sep 2014 20:10:51 +0400 Subject: [PATCH] Improve Streams implementation for performance and validity (based on JMH research) --- libraries/stdlib/src/generated/_Filtering.kt | 19 +- .../stdlib/src/kotlin/collections/Stream.kt | 355 +++++++++++++----- .../src/templates/Filtering.kt | 19 +- 3 files changed, 275 insertions(+), 118 deletions(-) diff --git a/libraries/stdlib/src/generated/_Filtering.kt b/libraries/stdlib/src/generated/_Filtering.kt index c90b3373d98..b7fd46ff924 100644 --- a/libraries/stdlib/src/generated/_Filtering.kt +++ b/libraries/stdlib/src/generated/_Filtering.kt @@ -163,8 +163,7 @@ public fun Iterable.drop(n: Int): List { * Returns a stream containing all elements except first *n* elements */ public fun Stream.drop(n: Int): Stream { - var count = 0; - return FilteringStream(this) { count++ >= n } + return DropStream(this, n) } /** @@ -338,16 +337,7 @@ public inline fun Iterable.dropWhile(predicate: (T) -> Boolean): List * Returns a stream containing all elements except first elements that satisfy the given *predicate* */ public fun Stream.dropWhile(predicate: (T) -> Boolean): Stream { - var yielding = false - return FilteringStream(this) { - if (yielding) - true - else if (!predicate(it)) { - yielding = true - true - } else - false - } + return DropWhileStream(this, predicate) } /** @@ -1058,8 +1048,7 @@ public fun Iterable.take(n: Int): List { * Returns a stream containing first *n* elements */ public fun Stream.take(n: Int): Stream { - var count = 0 - return LimitedStream(this) { count++ == n } + return TakeStream(this, n) } /** @@ -1203,7 +1192,7 @@ public inline fun Iterable.takeWhile(predicate: (T) -> Boolean): List * Returns a stream containing first elements satisfying the given *predicate* */ public fun Stream.takeWhile(predicate: (T) -> Boolean): Stream { - return LimitedStream(this, false, predicate) + return TakeWhileStream(this, predicate) } /** diff --git a/libraries/stdlib/src/kotlin/collections/Stream.kt b/libraries/stdlib/src/kotlin/collections/Stream.kt index 5ce7c097d01..fbeecbe32a0 100644 --- a/libraries/stdlib/src/kotlin/collections/Stream.kt +++ b/libraries/stdlib/src/kotlin/collections/Stream.kt @@ -1,6 +1,5 @@ package kotlin -import kotlin.support.AbstractIterator import java.util.* public trait Stream { @@ -9,144 +8,324 @@ public trait Stream { public fun streamOf(vararg elements: T): Stream = elements.stream() -public class FilteringStream( - private val stream: Stream, private val sendWhen: Boolean = true, private val predicate: (T) -> Boolean -) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { - val iterator = stream.iterator() - override fun computeNext() { +public fun streamOf(progression: Progression): Stream = object : Stream { + override fun iterator(): Iterator = progression.iterator() +} + +public class FilteringStream(private val stream: Stream, + private val sendWhen: Boolean = true, + private val predicate: (T) -> Boolean + ) : Stream { + + override fun iterator(): Iterator = object : Iterator { + val iterator = stream.iterator(); + var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue + var nextItem: T? = null + + private fun calcNext() { while (iterator.hasNext()) { val item = iterator.next() if (predicate(item) == sendWhen) { - setNext(item) + nextItem = item + nextState = 1 return } } - done() + nextState = 0 + } + + override fun next(): T { + if (nextState == -1) + calcNext() + if (nextState == 0) + throw NoSuchElementException() + val result = nextItem + nextItem = null + nextState = -1 + return result as T + } + + override fun hasNext(): Boolean { + if (nextState == -1) + calcNext() + return nextState == 1 } } } public class TransformingStream(private val stream: Stream, private val transformer: (T) -> R) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { + override fun iterator(): Iterator = object : Iterator { val iterator = stream.iterator() - override fun computeNext() { - if (iterator.hasNext()) { - setNext(transformer(iterator.next())) - } else { - done() - } + override fun next(): R { + return transformer(iterator.next()) + } + override fun hasNext(): Boolean { + return iterator.hasNext() } } } -public class MergingStream( - private val stream1: Stream, private val stream2: Stream, private val transform: (T1, T2) -> V -) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { +public class MergingStream(private val stream1: Stream, + private val stream2: Stream, + private val transform: (T1, T2) -> V + ) : Stream { + override fun iterator(): Iterator = object : Iterator { val iterator1 = stream1.iterator() val iterator2 = stream2.iterator() - override fun computeNext() { - if (iterator1.hasNext() && iterator2.hasNext()) { - setNext(transform(iterator1.next(), iterator2.next())) - } else { - done() - } + override fun next(): V { + return transform(iterator1.next(), iterator2.next()) + } + override fun hasNext(): Boolean { + return iterator1.hasNext() && iterator2.hasNext() } } } -public class FlatteningStream( - private val stream: Stream, private val transformer: (T) -> Stream -) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { +public class FlatteningStream(private val stream: Stream, + private val transformer: (T) -> Stream + ) : Stream { + override fun iterator(): Iterator = object : Iterator { val iterator = stream.iterator() var itemIterator: Iterator? = null - override fun computeNext() { + + override fun next(): R { + if (!ensureItemIterator()) + throw NoSuchElementException() + return itemIterator!!.next() + } + + override fun hasNext(): Boolean { + return ensureItemIterator() + } + + private fun ensureItemIterator(): Boolean { + if (itemIterator?.hasNext() == false) + itemIterator = null + while (itemIterator == null) { if (!iterator.hasNext()) { - done() - break; + return false } else { val element = iterator.next() val nextItemIterator = transformer(element).iterator() - if (nextItemIterator.hasNext()) + if (nextItemIterator.hasNext()) { itemIterator = nextItemIterator + return true + } } } - - val currentItemIterator = itemIterator - if (currentItemIterator == null) { - done() - } else { - setNext(currentItemIterator.next()) - if (!currentItemIterator.hasNext()) - itemIterator = null - } + return true } } } -public class Multistream(private val streams: Stream>) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { - val iterator = streams.iterator() - var streamIterator: Iterator? = null - override fun computeNext() { - while (streamIterator == null) { - if (!iterator.hasNext()) { - done() - break; - } else { - val stream = iterator.next() - val nextStreamIterator = stream.iterator() - if (nextStreamIterator.hasNext()) - streamIterator = nextStreamIterator - } - } - - val currentStreamIterator = streamIterator - if (currentStreamIterator == null) { - done() - } else { - setNext(currentStreamIterator.next()) - if (!currentStreamIterator.hasNext()) - streamIterator = null - } - } - } -} - -public class LimitedStream( - private val stream: Stream, private val stopWhen: Boolean = true, private val predicate: (T) -> Boolean -) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { +public class Multistream(private val stream: Stream>) : Stream { + override fun iterator(): Iterator = object : Iterator { val iterator = stream.iterator() - override fun computeNext() { - if (!iterator.hasNext()) { - done() - } else { - val item = iterator.next() - if (predicate(item) == stopWhen) { - done() + var itemIterator: Iterator? = null + + override fun next(): T { + if (!ensureItemIterator()) + throw NoSuchElementException() + return itemIterator!!.next() + } + + override fun hasNext(): Boolean { + return ensureItemIterator() + } + + private fun ensureItemIterator(): Boolean { + if (itemIterator?.hasNext() == false) + itemIterator = null + + while (itemIterator == null) { + if (!iterator.hasNext()) { + return false } else { - setNext(item) + val element = iterator.next() + val nextItemIterator = element.iterator() + if (nextItemIterator.hasNext()) { + itemIterator = nextItemIterator + return true + } } } + return true + } + } +} + +public class TakeStream(private val stream: Stream, + private var count: Int + ) : Stream { + { + if (count < 0) + throw IllegalArgumentException("count should be non-negative, but is $count") + } + + override fun iterator(): Iterator = object : Iterator { + val iterator = stream.iterator(); + + override fun next(): T { + if (count == 0) + throw NoSuchElementException() + count-- + return iterator.next() + } + + override fun hasNext(): Boolean { + return count > 0 && iterator.hasNext() + } + } +} + +public class TakeWhileStream(private val stream: Stream, + private val predicate: (T) -> Boolean + ) : Stream { + override fun iterator(): Iterator = object : Iterator { + val iterator = stream.iterator(); + var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue + var nextItem: T? = null + + private fun calcNext() { + if (iterator.hasNext()) { + val item = iterator.next() + if (predicate(item)) { + nextState = 1 + nextItem = item + return + } + } + nextState = 0 + } + + override fun next(): T { + if (nextState == -1) + calcNext() // will change nextState + if (nextState == 0) + throw NoSuchElementException() + val result = nextItem as T + + // Clean next to avoid keeping reference on yielded instance + nextItem = null + nextState = -1 + return result + } + + override fun hasNext(): Boolean { + if (nextState == -1) + calcNext() // will change nextState + return nextState == 1 + } + } +} + +public class DropStream(private val stream: Stream, + private var count: Int + ) : Stream { + { + if (count < 0) + throw IllegalArgumentException("count should be non-negative, but is $count") + } + + override fun iterator(): Iterator = object : Iterator { + val iterator = stream.iterator(); + + // Shouldn't be called from constructor to avoid premature iteration + private fun drop() { + while (count > 0 && iterator.hasNext()) { + iterator.next() + count-- + } + } + + override fun next(): T { + drop() + return iterator.next() + } + + override fun hasNext(): Boolean { + drop() + return iterator.hasNext() + } + } +} + +public class DropWhileStream(private val stream: Stream, + private val predicate: (T) -> Boolean + ) : Stream { + + override fun iterator(): Iterator = object : Iterator { + val iterator = stream.iterator(); + var dropState: Int = -1 // -1 for not dropping, 1 for nextItem, 0 for normal iteration + var nextItem: T? = null + + private fun drop() { + while (iterator.hasNext()) { + val item = iterator.next() + if (!predicate(item)) { + nextItem = item + dropState = 1 + return + } + } + dropState = 0 + } + + override fun next(): T { + if (dropState == -1) + drop() + + if (dropState == 1) { + val result = nextItem as T + nextItem = null + dropState = 0 + return result + } + return iterator.next() + } + + override fun hasNext(): Boolean { + if (dropState == -1) + drop() + return dropState == 1 || iterator.hasNext() } } } public class FunctionStream(private val producer: () -> T?) : Stream { - override fun iterator(): Iterator = object : AbstractIterator() { + override fun iterator(): Iterator = object : Iterator { + var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue + var nextItem: T? = null - override fun computeNext() { + private fun calcNext() { val item = producer() if (item == null) { - done() + nextState = 0 } else { - setNext(item) + nextState = 1 + nextItem = item } } + + override fun next(): T { + if (nextState == -1) + calcNext() + if (nextState == 0) + throw NoSuchElementException() + val result = nextItem as T + // Clean next to avoid keeping reference on yielded instance + nextItem = null + nextState = -1 + return result + } + + override fun hasNext(): Boolean { + if (nextState == -1) + calcNext() + return nextState == 1 + } + } } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt index 4e041edd93c..61e65b31e11 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt @@ -23,8 +23,7 @@ fun filtering(): List { returns(Streams) { "Stream" } body(Streams) { """ - var count = 0; - return FilteringStream(this) { count++ >= n } + return DropStream(this, n) """ } @@ -69,8 +68,7 @@ fun filtering(): List { returns(Streams) { "Stream" } body(Streams) { """ - var count = 0 - return LimitedStream(this) { count++ == n } + return TakeStream(this, n) """ } @@ -126,16 +124,7 @@ fun filtering(): List { returns(Streams) { "Stream" } body(Streams) { """ - var yielding = false - return FilteringStream(this) { - if (yielding) - true - else if (!predicate(it)) { - yielding = true - true - } else - false - } + return DropWhileStream(this, predicate) """ } @@ -174,7 +163,7 @@ fun filtering(): List { returns(Streams) { "Stream" } body(Streams) { """ - return LimitedStream(this, false, predicate) + return TakeWhileStream(this, predicate) """ } }