Start learning free

LESSON 97 OF 348 · ARRAYS I

Exceeding Static Array Bounds in Kotlin

Lesson Read the full explanation

When accessing array data with indices, the most common problem we can run into is exceeding the array's bounds.

Remember, the items of an array are indexed from 0 to length - 1. Any index which is not in this range is invalid.

When you try to access an item with an invalid index, Kotlin raises an exception:

val ourArray = intArrayOf(50, 40, 30)

// Valid indices: 0, 1, 2
println(ourArray[0]) // Output: 50
println(ourArray[2]) // Output: 30

// Invalid indices: 3, 30, -1
println(ourArray[3]) // throws IndexOutOfBoundsException
println(ourArray[30]) // throws IndexOutOfBoundsException
println(ourArray[-1]) // throws IndexOutOfBoundsException

As programmers, we always want to make sure that we don't exceed one array's bounds in our programs.


Assignment
Follow the Coding Tutorial and let's play with some arrays.


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    KOTLIN · ARRAYS I

    Exceeding Static Array Bounds in Kotlin, explained

    Valid and invalid indices

    An array of size n has valid indices 0 through n - 1. Any other index is outside the array's bounds. Kotlin checks every access, so an invalid index never reads some nearby memory: the program throws an exception instead.

    val ourArray = intArrayOf(50, 40, 30)
    
    println(ourArray[0])
    println(ourArray[2])
    println(ourArray[3])
    
    50
    30
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
    

    The first two lines work, then the third stops the program. The message names the bad index and the array's length. For an array, the exception is ArrayIndexOutOfBoundsException, a kind of IndexOutOfBoundsException. Indexes 30 and -1 fail the same way: -1 doesn't mean "the last element" in Kotlin.

    Why this is a common bug

    The mistake usually comes from the gap between a size and an index. An array of 3 elements has size 3, but the highest index is 2. Writing ourArray[ourArray.size] asks for the element after the last one.

    The exercise

    The starter code was meant to print the last element of ages but throws an exception:

    val ages = intArrayOf(14, 32, 50, 21)
    println(ages[ages.size])
    

    The array has four elements, so ages.size is 4 and the valid indices are 0 to 3. The index 4 is one past the end, which is what fails. The last element is at ages.size - 1:

    val ages = intArrayOf(14, 32, 50, 21)
    println(ages[ages.size - 1])
    

    This prints 21. Writing ages[3] would print the same here, but ages.size - 1 stays right if the array changes length. The shortcuts ages.last() and ages[ages.lastIndex] also give 21.

    A loop that goes one step too far

    Out-of-bounds errors often appear at the end of a loop. The range 0..ages.size includes the size, so the loop makes one pass too many:

    val ages = intArrayOf(14, 32, 50, 21)
    try {
        for (i in 0..ages.size) {
            println(ages[i])
        }
    } catch (e: ArrayIndexOutOfBoundsException) {
        println("caught: " + e.message)
    }
    
    14
    32
    50
    21
    caught: Index 4 out of bounds for length 4
    

    All four valid elements print, and then the access at index 4 throws. Catching the exception, as above, only demonstrates it; the real fix is the range 0 until ages.size.

    Lists fail the same way

    A MutableList checks its indices too. Reading index 5 of a two-item list throws IndexOutOfBoundsException with the message Index 5 out of bounds for length 2. The arrays in this lesson throw the more specific ArrayIndexOutOfBoundsException, which is a subclass of it.

    Reading the error message

    The exception text carries the two numbers you need: the index that was requested and the length of the array. In Index 4 out of bounds for length 4, the program asked for index 4 in an array of length 4. Compare the two: the index must be below the length. If it is equal, you are one past the end, as in the exercise. If it is much larger or negative, look for a calculation that produced the wrong number.

    In the console you also see a stack trace, which lists the function calls that led to the error. The first line that names your own code is where to look.

    Staying inside the bounds

    • Compute the last index from the size. Use size - 1 instead of a typed number.
    • Loop with the right range. for (i in 0 until ages.size) and for (i in ages.indices) stop at the last valid index. A range written as 0..ages.size includes the size itself and fails on the last pass.
    • Watch for empty arrays. An empty array has no valid index, and size - 1 is -1. Check ages.isNotEmpty() before reading the last element.
    • Use a safe read when a miss is acceptable. ages.getOrNull(10) returns null instead of throwing.

    Common mistakes

    • Using the size as the index. This is the mistake the exercise shows.
    • Assigning outside the bounds. ages[4] = 7 throws as well. An array never grows to fit a new index.
    • Catching the exception to hide a bug. It is better to fix the index than to wrap the access in try.

    Key takeaways

    • Valid indices run from 0 to size - 1.
    • An invalid index throws ArrayIndexOutOfBoundsException, with a message naming the index and the length.
    • array[array.size - 1] reads the last element.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free