Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 4

Iterating with `for` Loops

Recommended resources

Hello! Welcome to the fourth lesson in our "Learn Kotlin Syntax" course.

In our last lesson, we explored how Kotlin's string templates and multiline strings make text manipulation clean and intuitive. You learned to embed variables and expressions directly into strings, a significant improvement over traditional concatenation.

Today, we'll move from handling data to controlling program flow. The objective of this lesson is to learn how to write a 'for' loop to iterate over ranges, progressions, and collections. As a Java developer, you're familiar with for loops, but you'll find Kotlin's for-in loop to be a more powerful and versatile construct that applies uniformly to many different kinds of sequences.

1. The for Loop: Iterating Over Collections

In Kotlin, the for loop is exclusively a "for-each" style loop, similar to Java's enhanced for loop. It iterates over anything that provides an iterator—this includes collections, arrays, ranges, and even strings.

The basic syntax is straightforward: for (item in collection) { ... }.

Let's start by looking at the most common use case: iterating over a list.

Conditions and loops

The official Kotlin documentation provides a clear, simple example of the basic for loop syntax used with a list.

In this resource from kotlinlang.org, read the short section titled 'For loops'. It shows the fundamental syntax that we'll build upon.

As you saw, the syntax is clean and readable:

val items = listOf("apple", "banana", "kiwi")
for (item in items) {
    println(item)
}

This is the idiomatic way to loop in Kotlin. While you can use the Iterator interface manually with a while loop (as you might in older Java code), it's verbose and generally unnecessary. The for loop handles the iterator logic for you under the hood.

This next video provides an excellent comparison between the manual iterator approach and the idiomatic Kotlin for loop.

Iterator (and forEach) Explained - Kotlin Collections

This video from Cody Engel clearly demonstrates why the Kotlin for loop is preferred over manual iteration.

Watch from 00:58 to 05:35. The first part (until 03:44) shows the verbose while (iterator.hasNext()) pattern, which will be familiar from Java. The second part introduces the concise for (item in collection) loop, highlighting its safety and readability.


2. Iterating Over Ranges and Progressions

A powerful feature of Kotlin's for loop is its ability to iterate over numerical sequences. This is where it goes beyond Java's enhanced for loop and often replaces the classic C-style for (int i = 0; i < n; i++). These sequences are created using ranges and progressions.

This video provides a comprehensive tour of the different ways to create and control these sequences.

Kotlin for Beginners - Part 10 - FOR-LOOP & RANGES

This video from Coding in Flow is a perfect guide to ranges and progressions. It covers everything from simple ranges to custom steps.

Watch from 04:19 to 08:11. This covers the essential syntax for creating sequences: Inclusive ranges with the .. operator (e.g., 1..10). Exclusive ranges with until and counting down with downTo. Custom steps using the step keyword.

Let's summarize the key tools for creating these sequences:

  • .. (Range Operator): Creates an inclusive range. 1..4 includes 1, 2, 3, and 4.
    for (i in 1..4) {
        print("$i ") // Prints: 1 2 3 4 
    }
    
  • ..< or until: Creates an exclusive range (the end value is not included). 1..<4 and 1 until 4 are equivalent and include 1, 2, and 3. The ..< operator was introduced in Kotlin 1.8 and is becoming more common.
    for (i in 1 until 4) {
        print("$i ") // Prints: 1 2 3 
    }
    
  • downTo: Iterates in reverse order.
    for (i in 4 downTo 1) {
        print("$i ") // Prints: 4 3 2 1 
    }
    
  • step: Specifies the increment for the iteration. It can be combined with any of the above.
    for (i in 1..10 step 2) {
        print("$i ") // Prints: 1 3 5 7 9 
    }
    
    for (i in 10 downTo 0 step 3) {
        print("$i ") // Prints: 10 7 4 1 
    }
    

These constructs—ranges modified by downTo and step—are technically called progressions. A progression is defined by a start value, an end value, and a step. You can find more details in the official documentation on Ranges and progressions, but the key takeaway is that for loops can iterate over them seamlessly.


3. Iterating with an Index

A common requirement is to access the index of the element during iteration. In Java, this is the primary use case for the classic for loop. Kotlin provides two idiomatic ways to achieve this without resorting to a manual counter variable.

Let's turn back to the official documentation to see how.

Conditions and loops

This part of the documentation shows the two standard Kotlin patterns for accessing the index within a loop.

Read the subsection titled 'Arrays' (the concepts apply to lists as well). Focus on the two examples: one using the indices property and the other using the .withIndex() function.

Let's break down these two approaches:

  1. Using the indices property:
    Every array and list has an indices property, which is simply a range of its valid indices (e.g., 0..2 for a 3-element list). You can iterate over this range to get the index. This is useful when you primarily need the index to access elements.

    val items = listOf("A", "B", "C")
    for (i in items.indices) {
        println("Item at index $i is ${items[i]}")
    }
    

    Output:

    Item at index 0 is A
    Item at index 1 is B
    Item at index 2 is C
    
  2. Using the withIndex() function:
    This is often the more elegant solution. The withIndex() function returns a sequence of pairs, each containing an index and the value at that index. You can then use a destructuring declaration to unpack this pair directly into two variables.

    val items = listOf("A", "B", "C")
    for ((index, value) in items.withIndex()) {
        println("Item at index $index is $value")
    }
    

    The output is identical, but the code is arguably more readable as you don't need to perform the items[i] lookup yourself.


4. Hands-On Practice

Time to put it all together. In your IDE, create a main function and solve the following small problem.

Goal: Simulate processing a list of tasks from a queue.

  1. Create a val named taskIds and initialize it with a listOf some strings, for example: listOf("a-101", "b-205", "c-333", "d-404", "e-512").
  2. First, log all tasks:
    • Write a for loop that iterates through taskIds using withIndex().
    • Inside the loop, print a message for each task, including its 1-based position and its ID. For example: Processing task 1/5: a-101.
  3. Next, simulate a cleanup process:
    • Add a blank println() for spacing.
    • Print a message like "--- Running cleanup on even-numbered tasks ---".
    • Write a for loop that iterates over the taskIds.indices range with a step of 2.
    • Inside this loop, print a message for each task being cleaned up. For example: Cleaning up task a-101.
Click to see a possible solution
fun main() {
    val taskIds = listOf("a-101", "b-205", "c-333", "d-404", "e-512")

    // 1. Log all tasks
    println("--- Processing all tasks ---")
    for ((index, id) in taskIds.withIndex()) {
        println("Processing task ${index + 1}/${taskIds.size}: $id")
    }

    println() // For spacing

    // 2. Run cleanup on even-numbered tasks (indices 0, 2, 4...)
    println("--- Running cleanup on even-numbered tasks ---")
    for (i in taskIds.indices step 2) {
        println("Cleaning up task ${taskIds[i]}")
    }
}

Conclusion

In this lesson, you've learned how to use Kotlin's versatile for loop to handle a wide variety of iteration tasks in a concise and readable way.

Key Takeaways:

  • Kotlin's for loop is a for-in (or for-each) loop that works on any Iterable object.
  • You can iterate over collections like List to process each element.
  • You can iterate over ranges of numbers using .. (inclusive) and ..< or until (exclusive).
  • You can create advanced progressions by combining ranges with downTo (for reverse order) and step (for custom increments).
  • To access the index during iteration, use either the collection.indices property or the more idiomatic collection.withIndex() function with a destructuring declaration.

Next Lesson Preview:

Now that you can control repetition with loops, we'll look at controlling decisions. In the next lesson, we will explore how Kotlin enhances the if statement, turning it into an expression that can return a value. This is another feature that helps write more concise and functional-style code.