Kotlin Ranges

Kotlin ranges represent an ordered sequence of values with defined start and end points. A range represents an ordered set of values with a defined start and end. By default, it increments by 1 at each step. Ranges implement the ClosedRange<T> interface and are inclusive by default, meaning both endpoints are included in the range.

The primary advantage of Kotlin ranges is their simplicity and readability. Instead of writing traditional loop constructs, you can express iterations and value checks more naturally using range syntax.

// Traditional approach (verbose)  
for (i = 0; i < 10; i++) {  
    println(i)  
}  
  
// Kotlin range approach (concise)  
for (i in 0..9) {  
    println(i)  
}  

Creating Kotlin Ranges

Range Operator (..)

The most common way to create a range is using the .. operator, which creates a closed-ended range including both start and end values:

val numberRange = 1..5  // Creates range: 1, 2, 3, 4, 5  
val charRange = 'a'..'e'  // Creates range: a, b, c, d, e  

Range Functions

Kotlin provides several functions for creating ranges:

rangeTo() Function

The rangeTo() function is equivalent to the .. operator:

val range1 = 1.rangeTo(5)  // Same as 1..5  

rangeUntil() Function

To create an open-ended range, call the .rangeUntil() function with the ..< operator. This includes the start value but excludes the end value:

val openRange = 1..<5  // Creates range: 1, 2, 3, 4 (excludes 5)  

downTo() Function

For creating descending ranges, use the downTo() function:

val descendingRange = 5 downTo 1  // Creates range: 5, 4, 3, 2, 1  

Range Properties and Methods

Essential Properties

Every Kotlin range has three fundamental properties:

  • first: The starting element of the range
  • last: The ending element of the range
  • step: The increment between consecutive values (default is 1)
val range = 2..8  
println(range.first)  // Output: 2  
println(range.last)   // Output: 8  
println(range.step)   // Output: 1  

contains() Method

Check if a value exists within a range using the contains() method or the in operator:

val range = 10..20  
println(15 in range)          // Output: true  
println(range.contains(25))   // Output: false  

isEmpty() Method

Determine if a range is empty:

val emptyRange = 5..3  // Invalid range  
println(emptyRange.isEmpty())  // Output: true  

Working with Steps

Default Step Behavior

By default, ranges increment by 1. You can modify this using the step() function:

val evenNumbers = 2..10 step 2  // Creates: 2, 4, 6, 8, 10  
val multiplesOfFive = 5..25 step 5  // Creates: 5, 10, 15, 20, 25  

Step with Descending Ranges

Combine downTo with step for custom descending progressions:

val countdown = 10 downTo 0 step 2  // Creates: 10, 8, 6, 4, 2, 0  

Accessing Step Properties

val steppedRange = 1..10 step 3  
println(steppedRange.first)  // Output: 1  
println(steppedRange.last)   // Output: 10  
println(steppedRange.step)   // Output: 3  

Range Types and Examples

Integer Ranges

Integer ranges are the most commonly used type:

// Basic integer range  
val basicRange = 1..10  
  
// Range with step  
val oddNumbers = 1..20 step 2  
  
// Descending range  
val reverseRange = 100 downTo 90  

Character Ranges

Create ranges of characters for alphabet operations:

val lowercase = 'a'..'z'  
val uppercase = 'A'..'Z'  
val subset = 'm'..'r'  // Creates: m, n, o, p, q, r  

Long Ranges

For working with larger numbers:

val longRange = 1000L..2000L  
val largeStepped = 0L..1000000L step 100000L  

Range Iteration Techniques

Basic For Loop Iteration

Ranges are particularly useful for iterating over for loops:

// Forward iteration  
for (i in 1..5) {  
    println("Number: $i")  
}  
  
// Reverse iteration    
for (i in 5 downTo 1) {  
    println("Countdown: $i")  
}  
  
// Step iteration  
for (i in 0..100 step 10) {  
    println("Decade: $i")  
}  

forEach Function

Use the forEach function for functional-style iteration:

(1..5).forEach { number ->  
    println("Processing: $number")  
}  
  
// Shortened syntax  
(1..5).forEach(::println)  

Iterator-Based Iteration

For more control, use iterators:

val range = 1..5  
val iterator = range.iterator()  
  
while (iterator.hasNext()) {  
    val value = iterator.next()  
    println("Iterator value: $value")  
}  

Advanced Range Operations

Range Reversal

Reverse any range using the reversed() function:

val original = 1..5  
val reversed = original.reversed()  // Creates: 5, 4, 3, 2, 1  
  
reversed.forEach { println(it) }  

Collection Operations on Ranges

Apply functional programming operations to ranges:

val range = 1..10  
  
// Filter even numbers  
val evenNumbers = range.filter { it % 2 == 0 }  
println(evenNumbers)  // Output: [2, 4, 6, 8, 10]  
  
// Map to squares  
val squares = range.map { it * it }  
println(squares)  // Output: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]  
  
// Reduce to sum  
val sum = range.reduce { acc, value -> acc + value }  
println(sum)  // Output: 55  
  
// Calculate statistics  
println("Min: ${range.minOrNull()}")      // Output: Min: 1  
println("Max: ${range.maxOrNull()}")      // Output: Max: 10  
println("Average: ${range.average()}")    // Output: Average: 5.5  
println("Count: ${range.count()}")        // Output: Count: 10  

until Function

Create exclusive ranges using until:

// Exclusive end range  
for (i in 1 until 5) {  
    println(i)  // Prints: 1, 2, 3, 4 (excludes 5)  
}  
  
// Array indexing example  
val array = arrayOf("A", "B", "C", "D", "E")  
for (index in 0 until array.size) {  
    println("${index}: ${array[index]}")  
}  

Conditional Range Checking

Using in Operator

Check value membership efficiently:

val validScores = 0..100  
val userScore = 85  
  
when {  
    userScore in validScores -> println("Valid score: $userScore")  
    else -> println("Invalid score: $userScore")  
}  

When Expression with Ranges

By integrating when with ranges, we have crisp and intelligible range checks, which enhances the readability of our Kotlin program:

fun evaluateGrade(score: Int): String {  
    return when (score) {  
        in 90..100 -> "Excellent"  
        in 80..89 -> "Good"  
        in 70..79 -> "Average"  
        in 60..69 -> "Below Average"  
        in 0..59 -> "Fail"  
        else -> "Invalid Score"  
    }  
}  
  
println(evaluateGrade(87))  // Output: Good  

Multiple Range Conditions

val temperature = 25  
  
val weather = when (temperature) {  
    in -10..0 -> "Freezing"  
    in 1..15 -> "Cold"  
    in 16..25 -> "Mild"  
    in 26..35 -> "Warm"  
    in 36..50 -> "Hot"  
    else -> "Extreme"  
}  
  
println("Weather condition: $weather")  // Output: Weather condition: Mild  

Custom Range Types

Creating Enum Ranges

It’s also possible to create a range over custom objects. For that, the only requirement is to extend the Comparable interface:

enum class Priority(val level: Int) : Comparable<Priority> {  
    LOW(1), MEDIUM(2), HIGH(3), CRITICAL(4);  
      
    override fun compareTo(other: Priority): Int = this.level.compareTo(other.level)  
}  
  
fun checkPriorityRange() {  
    val priorityRange = Priority.LOW..Priority.HIGH  
      
    println(Priority.MEDIUM in priorityRange)   // Output: true  
    println(Priority.CRITICAL in priorityRange) // Output: false  
}  

Date Range Example

import java.time.LocalDate  
  
fun createDateRange(): ClosedRange<LocalDate> {  
    val startDate = LocalDate.of(2025, 1, 1)  
    val endDate = LocalDate.of(2025, 12, 31)  
    return startDate..endDate  
}  
  
fun isDateInCurrentYear(date: LocalDate): Boolean {  
    val yearRange = createDateRange()  
    return date in yearRange  
}  

Range Performance Considerations

Memory Efficiency

Ranges are memory-efficient because they don’t store all values in memory. Instead, they calculate values on-demand during iteration:

// This doesn't create a million-element array in memory  
val largeRange = 1..1_000_000  
  
// Values are generated as needed  
for (i in largeRange step 100_000) {  
    println(i)  // Prints: 1, 100001, 200001, etc.  
}  

Optimized Iterations

Kotlin’s for loops and ranges are optimized, but follow these tips for performance: Use until or step to minimize iterations:

// Efficient: Uses until to avoid unnecessary iteration  
for (i in 0 until array.size) {  
    processElement(array[i])  
}  
  
// Efficient: Uses step to skip unnecessary values  
for (i in 0..1000 step 50) {  
    performExpensiveOperation(i)  
}  

Practical Range Applications

Array and List Operations

fun demonstrateArrayRanges() {  
    val numbers = arrayOf(10, 20, 30, 40, 50)  
      
    // Iterate through array indices  
    for (index in numbers.indices) {  
        println("Index $index: ${numbers[index]}")  
    }  
      
    // Process specific range of elements  
    for (index in 1..3) {  
        println("Element at $index: ${numbers[index]}")  
    }  
      
    // Reverse iteration through array  
    for (index in numbers.indices.reversed()) {  
        println("Reverse index $index: ${numbers[index]}")  
    }  
}  

String Processing

fun processStringWithRanges(text: String) {  
    // Process each character position  
    for (position in text.indices) {  
        val char = text[position]  
        println("Position $position: '$char'")  
    }  
      
    // Extract substring using range  
    val middleRange = 2..5  
    if (text.length > 5) {  
        val substring = text.substring(middleRange)  
        println("Middle portion: $substring")  
    }  
}  

Validation Functions

fun validateInput(value: Int): Boolean {  
    val validRange = 1..100  
    return value in validRange  
}  
  
fun categorizeAge(age: Int): String {  
    return when (age) {  
        in 0..12 -> "Child"  
        in 13..19 -> "Teenager"  
        in 20..64 -> "Adult"  
        in 65..120 -> "Senior"  
        else -> "Invalid Age"  
    }  
}  

Complete Working Example

Here’s a comprehensive example demonstrating various Kotlin range features:

import kotlin.random.Random  
  
fun main() {  
    println("=== Kotlin Ranges Demonstration ===\n")  
      
    // 1. Basic range creation and iteration  
    println("1. Basic Integer Range:")  
    val basicRange = 1..5  
    basicRange.forEach { print("$it ") }  
    println("\n")  
      
    // 2. Character ranges  
    println("2. Character Range:")  
    val charRange = 'A'..'E'  
    for (char in charRange) {  
        print("$char ")  
    }  
    println("\n")  
      
    // 3. Step functionality  
    println("3. Range with Step:")  
    val steppedRange = 2..20 step 3  
    steppedRange.forEach { print("$it ") }  
    println("\n")  
      
    // 4. Descending ranges  
    println("4. Descending Range:")  
    val descendingRange = 10 downTo 5  
    descendingRange.forEach { print("$it ") }  
    println("\n")  
      
    // 5. Open-ended ranges  
    println("5. Open-ended Range (until):")  
    for (i in 1 until 5) {  
        print("$i ")  
    }  
    println("\n")  
      
    // 6. Range properties  
    println("6. Range Properties:")  
    val propertiesRange = 10..50 step 5  
    println("First: ${propertiesRange.first}")  
    println("Last: ${propertiesRange.last}")  
    println("Step: ${propertiesRange.step}")  
    println()  
      
    // 7. Membership testing  
    println("7. Membership Testing:")  
    val testRange = 1..100  
    val randomNumber = Random.nextInt(1, 150)  
    println("Random number: $randomNumber")  
    println("Is in range 1..100: ${randomNumber in testRange}")  
    println()  
      
    // 8. Collection operations  
    println("8. Collection Operations on Range:")  
    val operationsRange = 1..10  
    val evenNumbers = operationsRange.filter { it % 2 == 0 }  
    val squares = operationsRange.map { it * it }  
    val sum = operationsRange.sum()  
      
    println("Original range: ${operationsRange.toList()}")  
    println("Even numbers: $evenNumbers")  
    println("Squares: $squares")  
    println("Sum: $sum")  
    println("Average: ${operationsRange.average()}")  
    println()  
      
    // 9. Conditional range checking  
    println("9. Grade Evaluation:")  
    val scores = listOf(95, 87, 76, 64, 43)  
    scores.forEach { score ->  
        val grade = when (score) {  
            in 90..100 -> "A"  
            in 80..89 -> "B"  
            in 70..79 -> "C"  
            in 60..69 -> "D"  
            else -> "F"  
        }  
        println("Score $score: Grade $grade")  
    }  
    println()  
      
    // 10. Advanced range manipulation  
    println("10. Advanced Range Operations:")  
    val baseRange = 1..20  
    val reversedRange = baseRange.reversed()  
    val filteredRange = baseRange.filter { it % 3 == 0 }  
      
    println("Original: ${baseRange.take(5).toList()}...")  
    println("Reversed: ${reversedRange.take(5).toList()}...")  
    println("Multiples of 3: $filteredRange")  
      
    // 11. Array indexing with ranges  
    println("\n11. Array Operations:")  
    val fruits = arrayOf("Apple", "Banana", "Cherry", "Date", "Elderberry")  
      
    println("All fruits:")  
    for (index in fruits.indices) {  
        println("  $index: ${fruits[index]}")  
    }  
      
    println("First three fruits:")  
    for (index in 0..2) {  
        if (index < fruits.size) {  
            println("  ${fruits[index]}")  
        }  
    }  
      
    // 12. Custom validation function  
    println("\n12. Age Category Validation:")  
    val ages = listOf(8, 16, 25, 45, 70, 150)  
    ages.forEach { age ->  
        val category = when (age) {  
            in 0..12 -> "Child"  
            in 13..19 -> "Teenager"  
            in 20..64 -> "Adult"  
            in 65..120 -> "Senior"  
            else -> "Invalid"  
        }  
        println("Age $age: $category")  
    }  
}  

Expected Output

When you run the complete example above, you’ll see:

=== Kotlin Ranges Demonstration ===  
  
1. Basic Integer Range:  
1 2 3 4 5   
  
2. Character Range:  
A B C D E   
  
3. Range with Step:  
2 5 8 11 14 17 20   
  
4. Descending Range:  
10 9 8 7 6 5   
  
5. Open-ended Range (until):  
1 2 3 4   
  
6. Range Properties:  
First: 10  
Last: 50  
Step: 5  
  
7. Membership Testing:  
Random number: 73  
Is in range 1..100: true  
  
8. Collection Operations on Range:  
Original range: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  
Even numbers: [2, 4, 6, 8, 10]  
Squares: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]  
Sum: 55  
Average: 5.5  
  
9. Grade Evaluation:  
Score 95: Grade A  
Score 87: Grade B  
Score 76: Grade C  
Score 64: Grade D  
Score 43: Grade F  
  
10. Advanced Range Operations:  
Original: [1, 2, 3, 4, 5]...  
Reversed: [20, 19, 18, 17, 16]...  
Multiples of 3: [3, 6, 9, 12, 15, 18]  
  
11. Array Operations:  
All fruits:  
  0: Apple  
  1: Banana  
  2: Cherry  
  3: Date  
  4: Elderberry  
First three fruits:  
  Apple  
  Banana  
  Cherry  
  
12. Age Category Validation:  
Age 8: Child  
Age 16: Teenager  
Age 25: Adult  
Age 45: Adult  
Age 70: Senior  
Age 150: Invalid  

Kotlin ranges provide an elegant and powerful way to work with sequences of values. From simple iterations to complex conditional logic, mastering ranges will significantly improve your Kotlin programming skills.