Post

Index & Range Operations in Kotlin

Index & Range Operations in Kotlin

Quick reference for index operations commonly needed in problem-solving.

Quick Reference Table

Property/FunctionApplicable OnReturnsExampleNotes
lastIndexString, Array, ListInt"abc".lastIndexReturns -1 if empty
indicesString, Array, ListIntRangelist.indicesRange from 0 to lastIndex
first()CollectionElist.first()Throws if empty
last()CollectionElist.last()Throws if empty
firstOrNull()CollectionE?list.firstOrNull()Returns null if empty
lastOrNull()CollectionE?list.lastOrNull()Returns null if empty
getOrNull()List, ArrayE?list.getOrNull(1)Safe index access
indexOf()List, Array, StringIntlist.indexOf(x)Returns -1 if not found
indexOfFirst{}CollectionIntlist.indexOfFirst { it > 0 }Returns -1 if none match
indexOfLast{}CollectionIntlist.indexOfLast { it > 0 }Returns -1 if none match
lastIndexOf()List, Array, StringIntlist.lastIndexOf(x)Returns -1 if not found

Common Use Cases

String Index Operations

1
2
3
4
5
6
7
val str = "kotlin"
str.lastIndex               // 5
str.indices                // 0..5
str.indexOf("t")          // 2
str.lastIndexOf("t")      // 2
str[str.lastIndex]        // 'n'
str.indexOfFirst { it.isUpperCase() }  // -1 (no uppercase)

List/Array Index Operations

1
2
3
4
5
6
7
8
9
10
11
val list = listOf(1, 2, 2, 3)
list.first()              // 1
list.last()               // 3
list.indexOf(2)           // 1 (first occurrence)
list.lastIndexOf(2)       // 2 (last occurrence)
list.getOrNull(10)        // null (safe access)
list.getOrElse(10) { -1 } // -1 (default value if index invalid)

// Finding elements with conditions
list.indexOfFirst { it > 1 }  // 1
list.indexOfLast { it > 1 }   // 3

Ranges and Loops

1
2
3
4
5
6
7
8
9
10
11
12
13
val list = listOf(1, 2, 3)

// Forward iteration
for (i in list.indices) { ... }         // 0..2
for (i in 0..list.lastIndex) { ... }    // 0..2
for (i in 0 until list.size) { ... }    // 0..2

// Backward iteration
for (i in list.lastIndex downTo 0) { ... }     // 2 downTo 0
for (i in list.indices.reversed()) { ... }      // 2 downTo 0

// Step iteration
for (i in list.indices step 2) { ... }         // 0, 2

2D Array Index Operations

1
2
3
4
5
6
7
val matrix = Array(3) { IntArray(4) }
val rows = matrix.indices            // 0..2
val cols = matrix[0].indices        // 0..3

// Safe 2D array access
fun Array<IntArray>.getOrNull(row: Int, col: Int): Int? =
    this.getOrNull(row)?.getOrNull(col)

Common Patterns for Problem Solving

Safe Element Access

1
2
3
4
5
6
7
// ✅ Safe first/last access
val first = list.firstOrNull() ?: 0
val last = list.lastOrNull() ?: 0

// ✅ Safe index access
val element = list.getOrNull(index) ?: defaultValue
val element2 = list.getOrElse(index) { defaultValue }

Finding Elements

1
2
3
4
5
6
// Find first/last matching indices
val firstEven = list.indexOfFirst { it % 2 == 0 }
val lastEven = list.indexOfLast { it % 2 == 0 }

// Find all indices of an element
val allIndices = list.indices.filter { i -> list[i] == target }

Window Operations

1
2
3
4
5
6
7
8
9
10
11
12
// Check bounds for sliding window
fun isValidWindow(start: Int, end: Int, size: Int) =
    start >= 0 && end <= size - 1

// Get valid neighbors in grid
fun getNeighbors(i: Int, j: Int, grid: Array<IntArray>): List<Pair<Int, Int>> {
    val dirs = listOf(-1 to 0, 1 to 0, 0 to -1, 0 to 1)
    return dirs.map { (di, dj) -> i + di to j + dj }
        .filter { (ni, nj) -> 
            ni in grid.indices && nj in grid[0].indices 
        }
}

Performance Tips

  • getOrNull() is more efficient than try-catch for invalid indices
  • Use indices property instead of creating ranges manually
  • indexOfFirst/Last stops at first match, more efficient than filtering
  • For large lists, avoid multiple calls to last() or lastIndex in loops

Remember: Always prefer null-safe operations and range checks when dealing with indices to avoid IndexOutOfBoundsException.

This post is licensed under CC BY 4.0 by the author.