# The power of Kotlin for-loop

This is a chapter from the book Kotlin Essentials. You can find it on LeanPub.

In Java and other older languages, a for-loop typically has three parts: the first initializes the variable before the loop starts; the second contains the condition for the execution of the code block; the third is executed after the code block.

// Java
for(int i=0; i < 5; i++){
System.out.println(i);
}


However, this is considered complicated and error-prone. Just consider a situation in which someone uses > or <= instead of <. Such a small difference is not easy to notice, but it essentially influences the behavior of this for-loop.

As an alternative to this classic for-loop, many languages have introduced a modern alternative for iterating over collections. This is why, in languages like Java or JavaScript, there are two completely different kinds of for-loops, both of which are defined with the same for keyword. Kotlin has simplified this. In Kotlin, we have one universal for-loop that can be expressively used to iterate over a collection, a map, a range of numbers, and much more.

In general, a for-loop is used in Kotlin to iterate over something that is iterable1.

We can iterate over lists or sets.

fun main() { val list = listOf("A", "B", "C") for (letter in list) { print(letter) } // Variable type can be explicit for (str: String in setOf("D", "E", "F")) { print(str) } } // ABCDEF

We can also iterate over any other object as long as it contains the iterator method with no parameters, plus the Iterator result type and the operator modifier. The easiest way to define this method is to make your class implement the Iterable interface.

fun main() { val tree = Tree( value = "B", left = Tree("A"), right = Tree("D", left = Tree("C")) ) for (value in tree) { print(value) // ABCD } } class Tree( val value: String, val left: Tree? = null, val right: Tree? = null, ) : Iterable<String> { override fun iterator(): Iterator<String> = iterator { if (left != null) yieldAll(left) yield(value) if (right != null) yieldAll(right) } }

The inferred variable type of the variable defined inside the for-loop comes from the Iterable type argument. When we iterate over Iterable<User>, the inferred element type will be User. When we iterate over Iterable<Long?>, the inferred element type will be Long?. The same applies to all other types.

This mechanism, which relies on Iterable, is really powerful and allows us to cover numerous use cases, one of the most notable of which is the use of ranges to express progressions.

### Ranges

In Kotlin, if you place two dots between two numbers, like 1..5, you create an IntRange. This class implements Iterable<Int>, so we can use it in a for-loop:

fun main() { for (i in 1..5) { print(i) } } // 12345

This solution is efficient as well as convenient because the Kotlin compiler optimizes its performance under the hood.

Ranges created with .. include the last value (which means they are closed ranges). If you want a range that stops before the last value, use the until infix function instead.

fun main() { for (i in 1 until 5) { print(i) } } // 1234

Both .. and until start with the value on their left and progress toward the right number in increments of one. If you use a bigger number on the left, the result is an empty range.

fun main() { for (i in 5..1) { print(i) } for (i in 5 until 1) { print(i) } } // (nothing is printed)

If you want to iterate in the other direction, from larger to smaller numbers, use the downTo function.

fun main() { for (i in 5 downTo 1) { print(i) } } // 54321

The default step in all those cases is 1. If you want to use a different step, you should use the step infix function.

### Summary

In this chapter, we've learned about using the for-loop. It is really simple and powerful in Kotlin, so it’s worth knowing how it works, even though it’s not used very often (due to Kotlin’s amazing functional features, which are often used instead).

Now, let's talk about one of the most important Kotlin improvements over Java: good support for handling nullability.

1:

Has the iterator operator method.

2:

Destructuring will be explained in more depth in the Data classes chapter.