Skip to content

Commit 390552d

Browse files
committed
Avoid extra hasNext in trailing
Once trailing was advanced, hasNext was needlessly delegated to the underlying iterator on every invocation.
1 parent a473cb3 commit 390552d

File tree

2 files changed

+42
-21
lines changed

2 files changed

+42
-21
lines changed

src/library/scala/collection/Iterator.scala

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,6 @@ object Iterator {
290290
}
291291
}
292292

293-
import Iterator.empty
294-
295293
/** Iterators are data structures that allow to iterate over a sequence
296294
* of elements. They have a `hasNext` method for checking
297295
* if there is a next element available, and a `next` method
@@ -357,6 +355,8 @@ import Iterator.empty
357355
trait Iterator[+A] extends TraversableOnce[A] {
358356
self =>
359357

358+
import Iterator.empty
359+
360360
def seq: Iterator[A] = this
361361

362362
/** Tests whether this iterator can provide another element.
@@ -760,34 +760,31 @@ trait Iterator[+A] extends TraversableOnce[A] {
760760
* -1 not yet accessed
761761
* 0 single element waiting in leading
762762
* 1 defer to self
763+
* 2 self.hasNext already
764+
* 3 exhausted
763765
*/
764766
private[this] var status = -1
765-
def hasNext = {
766-
if (status > 0) self.hasNext
767-
else {
768-
if (status == 0) true
769-
else if (myLeading.finish()) {
770-
status = 0
771-
true
772-
}
773-
else {
774-
status = 1
775-
myLeading = null
776-
self.hasNext
777-
}
778-
}
767+
def hasNext = status match {
768+
case 3 => false
769+
case 2 => true
770+
case 1 => if (self.hasNext) { status = 2 ; true } else { status = 3 ; false }
771+
case 0 => true
772+
case _ =>
773+
if (myLeading.finish()) { status = 0 ; true } else { status = 1 ; myLeading = null ; hasNext }
779774
}
780775
def next() = {
781776
if (hasNext) {
782-
if (status > 0) self.next()
783-
else {
777+
if (status == 0) {
784778
status = 1
785-
val ans = myLeading.trailer
779+
val res = myLeading.trailer
786780
myLeading = null
787-
ans
781+
res
782+
} else {
783+
status = 1
784+
self.next()
788785
}
789786
}
790-
else Iterator.empty.next()
787+
else empty.next()
791788
}
792789

793790
override def toString = "unknown-if-empty iterator"

test/junit/scala/collection/IteratorTest.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,28 @@ class IteratorTest {
325325
assertSameElements(List(10,11,13), scan)
326326
assertSameElements(List(10,-1,-1,-11,11,-2,-2,-13,13,-3), results)
327327
}
328+
@Test def `scan trailing avoids extra hasNext`(): Unit = {
329+
val it = new AbstractIterator[Int] {
330+
var i = 0
331+
var checkedAt = -1
332+
def hasNext =
333+
if (checkedAt == i) false
334+
else {
335+
checkedAt = i
336+
true
337+
}
338+
def next() = {
339+
i += 1
340+
i
341+
}
342+
}
343+
val (lo, hi) = it.span(_ < 3)
344+
assertTrue(lo.hasNext)
345+
assertEquals(1, lo.next())
346+
assertTrue(hi.hasNext)
347+
assertEquals(3, hi.next())
348+
assertTrue(hi.hasNext)
349+
assertTrue(hi.hasNext) // no longer delegated
350+
assertTrue(hi.hasNext)
351+
}
328352
}

0 commit comments

Comments
 (0)