Kotlin - Loops (for/while)

Kotlin Loops (for/while)

Loops allow you to execute code repeatedly. Learn Kotlin's for loops, while loops, iteration patterns, and loop control statements like break and continue.

for Loops

Key Concept: Kotlin's for loop is different from Java/C-style for loops. It's designed for iterating over collections and ranges using the iterator pattern.

Basic for Loop with Ranges

// Iterate over a range
for (i in 1..5) {
    println("Number: $i")
}
// Output: 1, 2, 3, 4, 5

// Until (exclusive end)
for (i in 1 until 5) {
    println("Number: $i")
}
// Output: 1, 2, 3, 4

// Downward iteration
for (i in 5 downTo 1) {
    println("Number: $i")
}
// Output: 5, 4, 3, 2, 1

// With step
for (i in 1..10 step 2) {
    println("Odd number: $i")
}
// Output: 1, 3, 5, 7, 9

Iterating Over Collections

// Iterate over lists
val fruits = listOf("apple", "banana", "cherry", "date")

for (fruit in fruits) {
    println("Fruit: $fruit")
}

// Iterate over arrays
val numbers = arrayOf(10, 20, 30, 40, 50)
for (number in numbers) {
    println("Number: $number")
}

// Iterate over strings (character by character)
val text = "Kotlin"
for (char in text) {
    println("Character: $char")
}

// Iterate over maps
val scores = mapOf("Alice" to 95, "Bob" to 87, "Charlie" to 92)
for ((name, score) in scores) {
    println("$name scored $score")
}
// Or with entry
for (entry in scores) {
    println("${entry.key} scored ${entry.value}")
}

for Loop with Indices

val languages = listOf("Kotlin", "Java", "Python", "JavaScript")

// Using indices
for (i in languages.indices) {
    println("Language $i: ${languages[i]}")
}

// Using withIndex()
for ((index, language) in languages.withIndex()) {
    println("Language $index: $language")
}

// Using forEachIndexed (alternative)
languages.forEachIndexed { index, language ->
    println("Language $index: $language")
}

// Custom index range
for (i in 1..languages.size) {
    println("Position $i: ${languages[i - 1]}")
}

while Loops

Basic while Loop

// Basic while loop
var counter = 1
while (counter <= 5) {
    println("Counter: $counter")
    counter++
}

// While with condition checking
var input: String? = null
while (input != "quit") {
    println("Enter 'quit' to exit or anything else to continue:")
    input = readlnOrNull()  // In real app, get user input
    if (input != "quit") {
        println("You entered: $input")
    }
}

// Processing collections with while
val numbers = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
while (numbers.isNotEmpty()) {
    val number = numbers.removeAt(0)
    if (number % 2 == 0) {
        println("Even number removed: $number")
    }
}

do-while Loop

// do-while executes at least once
var attempts = 0
var success = false

do {
    attempts++
    println("Attempt $attempts")
    // Simulate some operation
    success = (1..10).random() > 7  // 30% chance of success
    if (!success) {
        println("Failed, trying again...")
    }
} while (!success && attempts < 5)

if (success) {
    println("Success after $attempts attempts!")
} else {
    println("Failed after $attempts attempts")
}

// Menu system example
var choice: Int
do {
    println("""
        Menu:
        1. Option A
        2. Option B
        3. Exit
        Enter choice:
    """.trimIndent())
    
    choice = (1..3).random()  // Simulate user input
    println("Selected: $choice")
    
    when (choice) {
        1 -> println("Executing Option A")
        2 -> println("Executing Option B")
        3 -> println("Exiting...")
        else -> println("Invalid choice, try again")
    }
} while (choice != 3)

Loop Control Statements

break Statement

// Break out of loop early
for (i in 1..10) {
    if (i == 6) {
        println("Breaking at $i")
        break
    }
    println("Processing: $i")
}
// Output: 1, 2, 3, 4, 5, then "Breaking at 6"

// Break in while loop
var count = 0
while (true) {  // Infinite loop
    count++
    println("Count: $count")
    
    if (count >= 3) {
        println("Breaking infinite loop")
        break
    }
}

// Finding first match
val numbers = listOf(2, 4, 7, 8, 10, 13, 16)
var firstOdd: Int? = null

for (number in numbers) {
    if (number % 2 == 1) {
        firstOdd = number
        println("Found first odd number: $firstOdd")
        break
    }
}
if (firstOdd == null) {
    println("No odd numbers found")
}

continue Statement

// Skip current iteration
for (i in 1..10) {
    if (i % 2 == 0) {
        continue  // Skip even numbers
    }
    println("Odd number: $i")
}
// Output: 1, 3, 5, 7, 9

// Process only valid data
val data = listOf("", "hello", null, "world", "", "kotlin")
for (item in data) {
    if (item.isNullOrBlank()) {
        continue  // Skip empty/null items
    }
    println("Processing: ${item.uppercase()}")
}

// Skip based on condition
val scores = listOf(85, 92, 67, 78, 95, 88, 72)
for (score in scores) {
    if (score < 70) {
        println("Skipping failing score: $score")
        continue
    }
    println("Passing score: $score")
}

Labeled break and continue

// Nested loops with labels
outer@ for (i in 1..3) {
    inner@ for (j in 1..3) {
        if (i == 2 && j == 2) {
            println("Breaking outer loop at i=$i, j=$j")
            break@outer  // Break the outer loop
        }
        println("i=$i, j=$j")
    }
}

// Continue with labels
outer@ for (i in 1..3) {
    println("Outer loop i=$i")
    for (j in 1..3) {
        if (j == 2) {
            println("Continuing outer loop")
            continue@outer  // Continue the outer loop
        }
        println("  Inner loop j=$j")
    }
}

// Practical example: matrix processing
val matrix = arrayOf(
    arrayOf(1, 2, 3),
    arrayOf(4, 0, 6),  // Contains zero
    arrayOf(7, 8, 9)
)

matrixLoop@ for ((rowIndex, row) in matrix.withIndex()) {
    for ((colIndex, value) in row.withIndex()) {
        if (value == 0) {
            println("Found zero at position [$rowIndex][$colIndex]")
            break@matrixLoop  // Stop processing entire matrix
        }
        println("Processing value $value at [$rowIndex][$colIndex]")
    }
}

Advanced Loop Patterns

Iterating with Conditions

// Loop until condition met
fun findTarget(numbers: List, target: Int): Int? {
    for ((index, number) in numbers.withIndex()) {
        if (number == target) {
            return index
        }
    }
    return null
}

// Multiple exit conditions
fun processData(data: List): List {
    val results = mutableListOf()
    
    for (item in data) {
        // Multiple conditions for skipping
        when {
            item.isBlank() -> continue
            item.startsWith("#") -> continue  // Skip comments
            item.length > 50 -> {
                println("Item too long, stopping: ${item.take(20)}...")
                break
            }
            else -> results.add(item.uppercase())
        }
    }
    
    return results
}

val testData = listOf("hello", "", "#comment", "world", "kotlin", "programming")
println(processData(testData))

Functional Style Alternatives

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// Traditional loop
val evenNumbers = mutableListOf()
for (number in numbers) {
    if (number % 2 == 0) {
        evenNumbers.add(number * 2)
    }
}
println("Traditional: $evenNumbers")

// Functional style (preferred in Kotlin)
val functionalEven = numbers
    .filter { it % 2 == 0 }
    .map { it * 2 }
println("Functional: $functionalEven")

// forEach (when you need side effects)
numbers.forEach { number ->
    if (number % 2 == 0) {
        println("Even: $number")
    }
}

// Using sequences for lazy evaluation
val lazyResult = numbers.asSequence()
    .filter { it % 2 == 0 }
    .map { it * 2 }
    .take(3)
    .toList()
println("Lazy: $lazyResult")

Real-World Examples

Data Processing Loop

data class Student(val name: String, val grades: List)

fun processStudents(students: List) {
    for ((index, student) in students.withIndex()) {
        println("Processing student ${index + 1}: ${student.name}")
        
        if (student.grades.isEmpty()) {
            println("  No grades available, skipping...")
            continue
        }
        
        var sum = 0
        var validGrades = 0
        
        for (grade in student.grades) {
            if (grade < 0 || grade > 100) {
                println("  Invalid grade: $grade, skipping...")
                continue
            }
            sum += grade
            validGrades++
        }
        
        if (validGrades == 0) {
            println("  No valid grades found")
            continue
        }
        
        val average = sum.toDouble() / validGrades
        println("  Average: %.2f".format(average))
        
        if (average >= 90) {
            println("  Grade: A (Excellent!)")
        } else if (average >= 80) {
            println("  Grade: B (Good)")
        } else if (average >= 70) {
            println("  Grade: C (Satisfactory)")
        } else {
            println("  Grade: F (Needs improvement)")
        }
        println()
    }
}

val students = listOf(
    Student("Alice", listOf(95, 87, 92, 89)),
    Student("Bob", listOf(78, 82, 85)),
    Student("Charlie", listOf()),  // No grades
    Student("Diana", listOf(67, 72, 69, 75))
)

processStudents(students)

Game Loop Example

class GameEngine {
    private var running = true
    private var score = 0
    private var level = 1
    
    fun gameLoop() {
        println("Game started!")
        
        gameRunning@ while (running) {
            println("\n--- Level $level ---")
            
            // Simulate level
            for (round in 1..5) {
                println("Round $round")
                
                // Simulate game action
                val success = (1..10).random() > 3  // 70% success rate
                
                if (success) {
                    score += 10
                    println("Success! Score: $score")
                } else {
                    println("Failed this round")
                    if (score > 0) score -= 5
                    
                    // Check game over condition
                    if (score < 0) {
                        println("Game Over! Final score: $score")
                        break@gameRunning
                    }
                }
                
                // Random events
                val event = (1..20).random()
                when (event) {
                    1 -> {
                        println("Bonus round! Double points!")
                        score += 20
                    }
                    2 -> {
                        println("Challenge failed! Lose points!")
                        score -= 15
                    }
                }
            }
            
            // Level completion
            if (score >= level * 50) {
                level++
                println("Level up! Now on level $level")
            } else {
                println("Level failed. Try again!")
                continue@gameRunning
            }
            
            // Check win condition
            if (level > 5) {
                println("Congratulations! You won the game!")
                running = false
            }
        }
        
        println("Final Stats - Level: $level, Score: $score")
    }
}

// Run the game
val game = GameEngine()
game.gameLoop()

File Processing with Error Handling

fun processLogFile(lines: List): Map {
    val errorCounts = mutableMapOf()
    var lineNumber = 0
    
    lineLoop@ for (line in lines) {
        lineNumber++
        
        // Skip empty lines and comments
        if (line.isBlank() || line.startsWith("#")) {
            continue
        }
        
        // Process log entry
        val parts = line.split(" ", limit = 3)
        
        if (parts.size < 3) {
            println("Warning: Invalid log format at line $lineNumber: $line")
            continue
        }
        
        val (timestamp, level, message) = parts
        
        // Only process error and warning levels
        when (level.uppercase()) {
            "ERROR", "WARN", "WARNING" -> {
                // Extract error type from message
                val errorType = message.split(":").firstOrNull()?.trim() ?: "Unknown"
                errorCounts[errorType] = errorCounts.getOrDefault(errorType, 0) + 1
            }
            "DEBUG", "TRACE" -> continue  // Skip debug messages
            "INFO" -> {
                // Just count info messages
                errorCounts["INFO"] = errorCounts.getOrDefault("INFO", 0) + 1
            }
            else -> {
                println("Unknown log level '$level' at line $lineNumber")
                continue
            }
        }
        
        // Stop if we find too many errors
        val totalErrors = errorCounts.filterKeys { it != "INFO" }.values.sum()
        if (totalErrors > 100) {
            println("Too many errors found, stopping processing")
            break@lineLoop
        }
    }
    
    return errorCounts
}

// Simulate log file
val logLines = listOf(
    "2023-01-01 INFO Application started",
    "2023-01-01 ERROR DatabaseConnection: Failed to connect",
    "2023-01-01 WARN MemoryUsage: High memory usage detected",
    "",
    "# This is a comment",
    "2023-01-01 ERROR NetworkTimeout: Request timed out",
    "2023-01-01 DEBUG Processing user request",
    "2023-01-01 ERROR DatabaseConnection: Failed to connect",
    "Invalid log line",
    "2023-01-01 INFO User logged in"
)

val results = processLogFile(logLines)
println("Error summary:")
results.forEach { (type, count) ->
    println("$type: $count occurrences")
}

Best Practices

✅ Good Practices

  • Use for loops for iterating over collections and ranges
  • Prefer functional alternatives (map, filter, forEach) when appropriate
  • Use meaningful variable names in loops
  • Keep loop bodies simple and focused
  • Use labeled breaks/continues for complex nested loops
  • Prefer while loops when the number of iterations is unknown

❌ Avoid

  • Complex logic inside loop conditions
  • Modifying collection while iterating (use iterator or functional methods)
  • Deeply nested loops without clear purpose
  • Infinite loops without proper exit conditions
  • Using loops when functional alternatives are clearer
Architecture Note: Kotlin encourages functional programming patterns. While loops are essential, consider using collection operations (map, filter, reduce) for data transformations as they're often more readable and less error-prone.

Practice Exercises

  1. Create a program that finds all prime numbers up to 100 using loops
  2. Implement a simple calculator that processes a list of operations
  3. Build a text analyzer that counts words, sentences, and paragraphs
  4. Create a multiplication table generator using nested loops
  5. Implement a simple game where players guess a number

Quick Quiz

  1. What's the difference between `1..5` and `1 until 5`?
  2. How do you iterate over both index and value of a list?
  3. What's the difference between `break` and `continue`?
  4. When should you use a while loop vs a for loop?
Show answers
  1. `1..5` includes 5 (closed range), `1 until 5` excludes 5 (half-open range)
  2. Use `list.withIndex()` or iterate over `list.indices`
  3. `break` exits the entire loop, `continue` skips to the next iteration
  4. Use for loops for known collections/ranges, while loops when the number of iterations is unknown