diff options
author | Paul Phillips <paulp@improving.org> | 2013-03-28 10:55:19 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-03-28 10:55:19 -0700 |
commit | c0eebbeef65216635c654018e8bb2382091d730a (patch) | |
tree | 377da2fe325f9888a0edf1df2c184f1f3ce6fd8a /src/library | |
parent | 79b18ccf9e9e9cb167ef9c2bdaf7e5adfa9a0ef0 (diff) | |
parent | e3ddb2d7dff859c9fb81d34d1c9687f72321a713 (diff) | |
download | scala-c0eebbeef65216635c654018e8bb2382091d730a.tar.gz scala-c0eebbeef65216635c654018e8bb2382091d730a.tar.bz2 scala-c0eebbeef65216635c654018e8bb2382091d730a.zip |
Merge pull request #2323 from paulp/pr/iterator_++
Iterator.++ no longer blows the stack.
Diffstat (limited to 'src/library')
-rw-r--r-- | src/library/scala/collection/Iterator.scala | 54 |
1 files changed, 36 insertions, 18 deletions
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index c85a4fb6e7..72a23a0dd0 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -161,6 +161,41 @@ object Iterator { def hasNext = true def next = elem } + + /** Avoid stack overflows when applying ++ to lots of iterators by + * flattening the unevaluated iterators out into a vector of closures. + */ + private[scala] final class ConcatIterator[+A](initial: Vector[() => Iterator[A]]) extends Iterator[A] { + // current set to null when all iterators are exhausted + private[this] var current: Iterator[A] = Iterator.empty + private[this] var queue: Vector[() => Iterator[A]] = initial + // Advance current to the next non-empty iterator + private[this] def advance(): Boolean = { + if (queue.isEmpty) { + current = null + false + } + else { + current = queue.head() + queue = queue.tail + current.hasNext || advance() + } + } + def hasNext = (current ne null) && (current.hasNext || advance()) + def next() = if (hasNext) current.next else Iterator.empty.next + + override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = + new ConcatIterator(queue :+ (() => that.toIterator)) + } + + private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] { + private[this] lazy val rhs: Iterator[A] = that.toIterator + def hasNext = lhs.hasNext || rhs.hasNext + def next = if (lhs.hasNext) lhs.next else rhs.next + + override def ++[B >: A](that: => GenTraversableOnce[B]) = + new ConcatIterator(Vector(() => this, () => that.toIterator)) + } } import Iterator.empty @@ -338,24 +373,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * @usecase def ++(that: => Iterator[A]): Iterator[A] * @inheritdoc */ - def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] { - // optimize a little bit to prevent n log n behavior. - private var cur : Iterator[B] = self - private var selfExhausted : Boolean = false - // since that is by-name, make sure it's only referenced once - - // if "val it = that" is inside the block, then hasNext on an empty - // iterator will continually reevaluate it. (ticket #3269) - lazy val it = that.toIterator - // the eq check is to avoid an infinite loop on "x ++ x" - def hasNext = cur.hasNext || (!selfExhausted && { - it.hasNext && { - cur = it - selfExhausted = true - true - } - }) - def next() = { hasNext; cur.next() } - } + def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.JoinIterator(self, that) /** Creates a new iterator by applying a function to all values produced by this iterator * and concatenating the results. |