Iterator HasNext Calls Next
ID |
kotlin.iterator_has_next_calls_next |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Kotlin |
Tags |
correctness, reliability |
Description
Reports calls to next() inside a hasNext() override. Calling next() from within
hasNext() advances the iterator on every existence check, silently skipping elements
and corrupting the iteration.
Rationale
The Iterator contract specifies that hasNext() must be idempotent — calling it
multiple times without an intervening next() must not change state. If hasNext()
calls next() internally, the following standard loop silently skips every other element:
// Bad
class IntIter(private val items: List<Int>) : Iterator<Int> {
private var index = 0
override fun hasNext(): Boolean {
return next() != null // FLAW — advances iterator on every check
}
override fun next(): Int = items[index++]
}
// Using it: prints only even-indexed elements
val it = IntIter(listOf(1, 2, 3, 4))
while (it.hasNext()) { // consumes items[0]=1
println(it.next()) // then consumes items[1]=2 — skips 1
}
// prints 2, 4 instead of 1, 2, 3, 4
// Good
override fun hasNext(): Boolean = index < items.size // no side-effects
Remediation
Rewrite hasNext() to check the iterator’s internal state (position, backing
collection size, peek buffer) without consuming an element. If look-ahead is
necessary, read one element ahead into a nullable field and check it there —
but never via a next() call in hasNext().