Permalink
Browse files

SI-8428 Fix regression in iterator concatenation

Regressed in e3ddb2d, which introduced `ConcatIterator` to
avoid super-linear runtime on chains of concatenated iterators.

`ConcatIterator` maintains a queue of thunks for the remaining
iterators. Both `next` and `hasNext` delegate to `advance`, which
evaluates the thunks and discards any empty iterators from the
start of the queue. The first non-empty iterator is stored in
the var `current`.

It also overrides `++`, and creates a new `ConcatIterator` with
the given `that` as an extra element in the queue. However, it
failed to copy `current` across, which led to data loss.
  • Loading branch information...
1 parent bcf24ec commit 1fa46a5a9634f09e4905da2468acf5ea75d6462e @retronym retronym committed Mar 20, 2014
Showing with 13 additions and 1 deletion.
  1. +1 −1 src/library/scala/collection/Iterator.scala
  2. +12 −0 test/files/run/t8428.scala
@@ -185,7 +185,7 @@ object Iterator {
def next() = if (hasNext) current.next else Iterator.empty.next
override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] =
- new ConcatIterator(queue :+ (() => that.toIterator))
+ new ConcatIterator((() => current) +: (queue :+ (() => that.toIterator)))
}
private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] {
@@ -0,0 +1,12 @@
+object Test extends App {
+ val xs = List.tabulate(4)(List(_))
+ val i = xs.map(_.iterator).reduce { (a,b) =>
+ a.hasNext
+ a ++ b
+ }
+
+ val r1 = i.toList
+ val r2 = xs.flatten.toList
+
+ assert(r1 == r2, r1)
+}

0 comments on commit 1fa46a5

Please sign in to comment.