The Programmers Guide To Kotlin- Iterators & Sequences
Written by Mike James   
Monday, 17 September 2018
Article Index
The Programmers Guide To Kotlin- Iterators & Sequences
Ranges & Progressions
Progressions

Iterators and sequences are a large part of using collections and taking a functional approach to programming. We look at how they work in Kotlin.

Programmer's Guide To Kotlin

Now Available as a Print Book

cover

You can buy it from: Amazon

Contents

Some Chapters Already Available On The Web

  1. What makes Kotlin Special (Book Only)
  2. The Basics: Variables, Primitive Types and Functions
  3. Control
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy 
            Extract  Type and its problems
            Extract  Smart Casts
  8. Generics
            Extract Basic Generics
            Extract Covariance & Contravariance
  9. Collections, Iterators, Sequences & Ranges
            Extract Iterators & Sequences
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
            Extract    Annoymous and Lambda Functions
            ExtractInline Functions  **NEW!**
  12. Data classes, enums and destructuring
            Extract  Data Classes
            Extract 
         Delegated Properties 
  13. Exceptions, Annotations & Reflection
            Extract Annotation & Reflection 
  14. Working with Java
            Extract Using Swing  

It is assumed that you know the basics of collections and in particular how Kotlin presents collections to the user. This is covered in detail in the first part of this chapter. Here we are only concerned with iterators and the related idea of a sequence.

Iterators

Collections of objects usually have an iterator to allow you to step through each object in turn. In fact, you can have an iterator that isn't associated with a collection of objects and simply generates the next object on demand.

An iterator has to have at least two functions – next which returns the next object, and hasNext which returns true if there is a next object.

For example the Kotlin for can be written as:

while(it.hasNext()){
 e=it.Next()
 instructions that use e
}

For example:

var a= mutableListOf<String>("a","b","c")
for(e in a.iterator()) println(e)
var it=a.iterator()
while(it.hasNext()){
   var e=it.next()
   println(e)
 }

The two loops work in the same way. The iterator method returns an initialized iterator for the collection. You can use this to retrieve each element in turn. Notice that you can't reset an iterator to the first value – you simply create a new instance. If this behavior doesn't suit you then simply include a start and/or end parameter in the constructor and modify the Next and hasNext methods accordingly.

Although iterators are generally linked to collection style data structures, they can also be used to generate sequences. In the case of a collection, the Next method retrieves the next item in the collection, but for a sequence it simply computes the next value.

For example a CountToTen class would be something like:

class CountToTen():Iterator<Int>{
    private var i:Int=1
    override fun next(): Int {
       return i++
    }    

   override fun hasNext(): Boolean {
       if(i>10) return false
        return true
    }
}

and it could be used anywhere you needed the sequence of numbers. For example:

val a=CountToTen()
for(e in a) println(e)

prints 1 to 10.

In most cases it would be better to create a class that implemented the Iterable interface. This has just one operator method, iterator, which returns an Iterator object for the class in question.

Notice that an iterator is "lazy" in the sense that it doesn't compute the complete sequence of values at once, it only computes a value when needed. Kotlin has a lot of facilities for functional programming and in functional programming you often chain together functions like iterators which produce sequences.

For efficiency, it is important that these put off creating a sequence until it is actually needed – i.e. they need to be lazy in an extended sense. Kotlin provides the Sequence<T> type to allow you to use and implement iterators that are lazy in this broader sense.

Sequences

Although functional programming isn't the focus of this book, it is worth giving an example of the way iterators and sequences differ, if only because non-functional programmers find it hard to understand why there are two so closely related types.

Compare:

val s= sequenceOf( 1,2,3,4,5,6,7,8,9,10 )
val ms=s.map({println(it);it})
println("end")

which uses a sequence and:

val i= listOf(1,2,3,4,5,6,7,8,9,10)
val mi=i.map({println(it);it})
println("end")

which uses the iterator associated with the List.

The map method simply applies the specified function, i.e. the println, to each of the elements of the sequence or collection. If you run this you will discover that the map acting on the sequence doesn't print anything, but the List iterator does. The reason is that in the case of the sequence, map returns an unevaluated sequence ready for further operations. In the case of the List, the map returns another List after evaluating everything. If you want to force the sequence to evaluate, you have to do something that makes use of its results.

For example:

val s= sequenceOf( 1,2,3,4,5,6,7,8,9,10 )
val ms=s.map({println(it);it})
println("end")
println(ms.last())

You will now see printed:

end
1
2
3
4
5
6
7
8
9
10
10

This unexpected order of events, i.e. the map happens after the “end” happens because the map isn’t evaluated until it is actually needed. So the assignment to ms doesn’t evaluate the map, it is simply stored ready to be evaluated when needed. Then “end” is printed and then the map is finally evaluated as its last element is needed. The evaluation is even more lazy than you might imagine in that that if you use first in place of last then only the first element is evaluated. The lazy evaluation is only performed to the point where the element that is actually needed becomes available.

This is aggressively lazy and it has to be to ensure that many functional forms of simple algorithms are practical.

cover



Last Updated ( Monday, 17 September 2018 )