Blog / December 20, 2021 / 7 mins read / By Suneet Agrawal

Iterators in Kotlin

Collections (Sets, Maps and Lists) are something we use daily. Traversing (Iteration) is the most common operation we perform over any collection.

Iterators are used to traverse over a collection object. It provides access to the elements of the collection object sequentially without exposing the underlying structure of the collection.

How to get an iterator object?

An iterator reference object can be obtained using iterator() function which is declared in the Iterable interface for generic type T. The Iterable interface is extended by the Collection interface which is implemented by all immutable Sets, Maps and Lists.

/**
 * Classes that inherit from this interface can be represented as a sequence of elements that can
 * be iterated over.
 * @param T the type of element being iterated over. The iterator is covariant in its element type.
 */
public interface Iterable<out T> {
    /**
     * Returns an iterator over the elements of this object.
     */
    public operator fun iterator(): Iterator<T>
}

The Iterable has one more variant interface as MutableIterable which has the same iterator() function but this interface is extended by MutableCollection which is implemented by mutable Sets, Maps and Lists.

/**
 * Classes that inherit from this interface can be represented as a sequence of elements that can
 * be iterated over and that supports removing elements during iteration.
 * @param T the type of element being iterated over. The mutable iterator is invariant in its element type.
 */
public interface MutableIterable<out T> : Iterable<T> {
    /**
     * Returns an iterator over the elements of this sequence that supports removing elements during iteration.
     */
    override fun iterator(): MutableIterator<T>
}

How to use an iterator object?

The object we get by calling the iterator() function points to the address of the first element of the collection. This is equivalent to the head of a LinkedList. Calling the next function on the iterator object will return the next element of that collection. We also have the hasNext function which returns a boolean if the iteration has more elements.

If the collection is empty, the first call to the hasNext function itself will return false. There is no need to handle any null pointer exception.

/**
 * An iterator over a collection or another entity that can be represented as a sequence of elements.
 * Allows to sequentially access the elements.
 */
public interface Iterator<out T> {
    /**
     * Returns the next element in the iteration.
     */
    public operator fun next(): T

    /**
     * Returns `true` if the iteration has more elements.
     */
    public operator fun hasNext(): Boolean
}

Similar to MutableIterable, we have a MutableIterator interface also which extends the Iterator interface and adds another function, remove() which provides the mutability functionality.

/**
 * An iterator over a mutable collection. Provides the ability to remove elements while iterating.
 * @see MutableCollection.iterator
 */
public interface MutableIterator<out T> : Iterator<T> {
    /**
     * Removes from the underlying collection the last element returned by this iterator.
     */
    public fun remove(): Unit
}

Example using iterator

There are three ways to use an iterator on all types of collections.

1. Using hasNext and next functions

Any collection can be iterated using the hasNext and next functions with a while loop.

Below are the examples for the same.

//Map
val map = mapOf(1 to "one", 2 to "two", 3 to "three")
val mapIterator = map.iterator()

while(mapIterator.hasNext()){
        val entry = mapIterator.next()
        println(entry.key.toString() + " : " + entry.value )
}

//Set
val set = setOf(1,2,3)
val setIterator = set.iterator()

while(setIterator.hasNext()){
        println(setIterator.next())
}

//List
val list = listOf(1,2,3)
val listIterator = list.iterator()

while(listIterator.hasNext()){
        println(listIterator.next())
}

Keep in mind, once the iterator passes through the last element, it can no longer be used for retrieving elements. Neither can it be reset to any previous position. In order to iterate through the collection again, we need to create a new iterator.

2. Using for-in loop

Any collection can also be iterated using a for-in loop.[For-in](blogs/kotlin-for-loop/" target=) doesn’t provide us with the iterable object but uses the iterator implicitly.

Below are the examples for the same.

//Map
val map = mapOf(1 to "one", 2 to "two", 3 to "three")
for(entry in map){
        println(entry.key.toString() + " : " + entry.value )
}

//Set
val set = setOf(1,2,3)
for(entry in set){
        println(entry)
}

//List
val list = listOf(1,2,3)
for(item in list){
        println(item)
}

3. Using for-each loop

Any collection can also be iterated using a for-each loop.[For-each](blogs/kotlin-for-loop/" target=) also doesn’t provide us with the iterable object but uses the iterator implicitly.

Below are the examples for the same.

//Map
val map = mapOf(1 to "one", 2 to "two", 3 to "three")
map.forEach{ entry ->
        println(entry.key.toString() + " : " + entry.value )
}

//Set
val set = setOf(1,2,3)
set.forEach{ entry ->
        println(entry)
}

//List
val list = listOf(1,2,3)
list.forEach{ item ->
        println(item)
}

List Iterators

In order to iterate over a list, we have another Iterator ie ListIterator which provides the additional functionality of traversing to the previous element also. It has hasPrevious and previous functions which can be used to traverse in the backward direction.

ListIterator also has nextIndex and previousIndex functions which return the index of the elements.

/**
 * An iterator over a collection that supports indexed access.
 * @see List.listIterator
 */
public interface ListIterator<out T> : Iterator<T> {
    // Query Operations
    override fun next(): T
    override fun hasNext(): Boolean

    /**
     * Returns `true` if there are elements in the iteration before the current element.
     */
    public fun hasPrevious(): Boolean

    /**
     * Returns the previous element in the iteration and moves the cursor position backwards.
     */
    public fun previous(): T

    /**
     * Returns the index of the element that would be returned by a subsequent call to [next].
     */
    public fun nextIndex(): Int

    /**
     * Returns the index of the element that would be returned by a subsequent call to [previous].
     */
    public fun previousIndex(): Int
}

Since ListIterator can be used to iterate in both the directions, it can be used even after reaching the last element.

Below is the example for the same.

//List
val list = listOf(1,2,3,4)
val listIterator = list.listIterator()

while (listIterator.hasNext()){
        println("Index : ${listIterator.nextIndex() } ; Element : ${listIterator.next()}")
}

while (listIterator.hasPrevious()){
        println("Index : ${listIterator.previousIndex() } ; Element : ${listIterator.previous()}")
}

Mutable List Iterators

In order to iterate over mutable list collection, we have another Iterator ie MutableListIterator which provides the additional functionality of adding, editing or removing to the existing collection while iteration itself. MutableListIterator extends ListIterator as well as MutableIterator.

/**
 * An iterator over a mutable collection that supports indexed access. Provides the ability
 * to add, modify and remove elements while iterating.
 */
public interface MutableListIterator<T> : ListIterator<T>, MutableIterator<T> {
    // Query Operations
    override fun next(): T
    override fun hasNext(): Boolean

    // Modification Operations
    override fun remove(): Unit

    /**
     * Replaces the last element returned by [next] or [previous] with the specified element [element].
     */
    public fun set(element: T): Unit

    /**
     * Adds the specified element [element] into the underlying collection immediately before the element that would be
     * returned by [next], if any, and after the element that would be returned by [previous], if any.
     * (If the collection contains no elements, the new element becomes the sole element in the collection.)
     * The new element is inserted before the implicit cursor: a subsequent call to [next] would be unaffected,
     * and a subsequent call to [previous] would return the new element. (This call increases by one the value \
     * that would be returned by a call to [nextIndex] or [previousIndex].)
     */
    public fun add(element: T): Unit
}
    Below is the example for the same.
//List
val list = mutableListOf(1,2,3,4)
val mutableIterator = list.listIterator()

mutableIterator.next()
mutableIterator.add(0)
println(list)

mutableIterator.next()
mutableIterator.remove()
println(list)

mutableIterator.next()
mutableIterator.set(10)
println(list)
Comments