Indexing a list or buffer
Scala sequences count positions from zero. The first element is at index 0, the second at index 1, and so on. You read an element by writing its index in parentheses after the collection's name:
val ages = List(14, 32, 50, 21)
println(ages(0)) // 14
println(ages(2)) // 50
The same syntax works for an Array and for an ArrayBuffer, the resizable collection from scala.collection.mutable. The call ages(2) is a method call to apply, which is why there are parentheses, not square brackets. Brackets are for type parameters, so ages[2] is a compile error.
The value you get back has the collection's element type. Elements of a collection of Int are ordinary integers, and you can add or compare them directly.
The exercise
This lesson uses two ArrayBuffer variables:
val fruits = scala.collection.mutable.ArrayBuffer("mango", "kiwi", "grapefruit")
val ages = scala.collection.mutable.ArrayBuffer(14, 32, 50, 21)
Step 1: the second fruit
The second item has index 1. println(fruits(1)) prints kiwi. Index 2 would give the third item, grapefruit.
Step 2: the sum of the first three ages
Index each of the first three positions and add them inside the call: println(ages(0) + ages(1) + ages(2)). Scala finishes the sum before println runs, so it prints 96.
Step 3: last minus first
There are four numbers, so the last is at index 3. println(ages(3) - ages(0)) prints 7.
The finished program
val fruits = scala.collection.mutable.ArrayBuffer("mango", "kiwi", "grapefruit")
val ages = scala.collection.mutable.ArrayBuffer(14, 32, 50, 21)
println(fruits(1))
println(ages(0) + ages(1) + ages(2))
println(ages(3) - ages(0))
Its output:
kiwi
96
7
Reading every element
To visit all elements, loop over the collection itself. You don't need to compute indexes at all:
for (age <- ages) {
println(age)
}
This prints 14, 32, 50 and 21, one per line. When the position matters as well as the value, loop over the indexes with for (i <- 0 until ages.size) and read ages(i). The range 0 until ages.size stops before the size, which keeps every index valid.
Reading does not change anything
Indexing only looks at an element. After println(fruits(1)), the buffer still holds the same three fruits in the same order. Changing an element is a different operation, assignment by index, which the next lessons cover.
Gotchas
- Off by one. Item number
nis at indexn - 1. The last index is the size minus one. - Out of range. An index that doesn't exist throws an
IndexOutOfBoundsExceptionat run time. For the four ages above,ages(10)fails with the message10 is out of bounds (min 0, max 3). - Shortcuts for the ends.
ages.headis the first element andages.lastis the last, soages.last - ages.headis also7. - Indexing a list is not free. An
ArrayBufferor anArrayjumps straight to an index. AListis a chain of cells, solist(i)walks from the front. That doesn't matter for four items, but it does for long lists inside loops.
Safe access with lift
When an index might be invalid, you can ask for an Option instead of risking an exception. The method lift turns a sequence into a function that returns Some(element) for a valid index and None for any other. With the four ages, ages.lift(1) is Some(32) and ages.lift(10) is None. Then you can write ages.lift(10).getOrElse(0) to fall back to a default value, which is 0 here.
Key takeaways
- Read an element with
collection(index), counting from 0. - The last index is
size - 1. - An invalid index throws an exception when the program runs.
