Kotlin Iterators
An iterator walks through a collection one item at a time. Every for loop over a list, set, or map uses an iterator behind the scenes. Learning the iterator directly gives you finer control over the walk, including the power to remove items safely.
The Cursor on a Conveyor Belt
Imagine a conveyor belt carrying parcels past a worker. The worker looks at one parcel, picks it up, and the belt moves ahead. The worker never jumps backward and never sees the whole belt at once. An iterator behaves like that worker. A pointer sits before the first item and moves forward on demand.
Pointer starts here
|
v
[ A ] [ B ] [ C ] [ D ]
^
next() returns A, pointer moves to the gap before B
The Two Core Functions
hasNext()answers yes or no: does another item wait ahead?next()returns the following item and moves the pointer.
fun main() {
val fruits = listOf("Apple", "Banana", "Cherry")
val iterator = fruits.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
}The loop prints Apple, Banana, and Cherry. Calling next() after the last item throws a NoSuchElementException, so always check hasNext() first.
The for Loop Shortcut
The compiler translates a for loop into the same iterator steps.
for (fruit in fruits) {
println(fruit)
}Both versions behave the same. The loop version needs less typing, and the manual version offers extra control.
Removing Items While Walking
Changing a list inside a normal for loop causes a ConcurrentModificationException. A mutable iterator avoids that problem. Its remove() function deletes the item returned by the latest next() call.
fun main() {
val numbers = mutableListOf(1, 2, 3, 4, 5, 6)
val iterator = numbers.iterator()
while (iterator.hasNext()) {
if (iterator.next() % 2 == 0) {
iterator.remove()
}
}
println(numbers) // [1, 3, 5]
}
Before: [1] [2] [3] [4] [5] [6]
| | |
remove() even even even
After: [1] [3] [5]
Walking Backward with ListIterator
A list offers a richer iterator. A ListIterator moves in both directions and reports positions.
val letters = listOf("A", "B", "C")
val it = letters.listIterator()
while (it.hasNext()) {
println("${it.nextIndex()}: ${it.next()}")
}
while (it.hasPrevious()) {
println(it.previous())
}The first loop prints 0: A, 1: B, and 2: C. The second loop walks back and prints C, B, and A.
Walking a Map
A map holds pairs of keys and values. The iterator delivers one pair at a time.
val prices = mapOf("Tea" to 20, "Coffee" to 35)
for ((item, price) in prices) {
println("$item costs $price")
}Make Your Own Class Loopable
Any class that supplies an iterator() function marked operator works inside a for loop. A countdown timer shows the idea.
class Countdown(private val from: Int) {
operator fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
var current = from
override fun hasNext() = current >= 0
override fun next() = current--
}
}
}
fun main() {
for (n in Countdown(3)) {
print("$n ") // 3 2 1 0
}
}The object keeps its own counter, so each loop starts fresh.
Iterator Types at a Glance
| Type | Moves | Can Remove |
|---|---|---|
| Iterator | Forward | No |
| MutableIterator | Forward | Yes |
| ListIterator | Both directions | No |
| MutableListIterator | Both directions | Yes, plus add and set |
For simple removal tasks, the function removeAll or removeIf offers a shorter route. Reach for a manual iterator when the walk needs custom control.
