Three ways to hold a sequence
Scala has several types for an ordered group of values. Three are common in beginner code, and they differ in whether the contents or the size can change:
| Type | Size | Elements |
|---|---|---|
List | fixed | cannot be changed |
Array | fixed | can be changed |
ArrayBuffer | can grow and shrink | can be changed |
All three keep their items in order and count positions from zero, so the first element is at index 0.
List
A List is immutable. Once built, it never changes:
val sandwich = List("peanut butter", "jelly", "bread")
val numbers = List(50, 60, 70)
val data = numbers(1) // 60
println(numbers.size) // 3
for (ingredient <- sandwich) {
println(ingredient)
}
The loop prints peanut butter, jelly and bread, one per line. Scala reads an element with parentheses, numbers(1). Square brackets are used for type parameters such as List[Int], so numbers[1] doesn't compile.
You can't assign to a list position: numbers(0) = 5 is a compile error, because List has no update method. To get a changed copy, call updated. sandwich.updated(1, "honey") returns a new list List(peanut butter, honey, bread) and leaves sandwich as it was.
Array
An Array has a fixed length, but you can assign to its positions. It is created with Array(...):
val arr = Array(1, 2, 3)
val first = arr(0) // 1
arr(0) = 10 // allowed
It is indexed the same way as a list, with parentheses.
ArrayBuffer: the resizable one
When you need a collection that can grow and shrink, use scala.collection.mutable.ArrayBuffer. It is the closest Scala type to a resizable array in other languages. You create it with its companion object, so there is no new, and the element type is inferred:
val fruits = scala.collection.mutable.ArrayBuffer("mango", "kiwi", "grapefruit")
fruits += "plum" // now 4 elements
fruits.remove(0) // removes "mango"
println(fruits) // ArrayBuffer(kiwi, grapefruit, plum)
+= appends an element, and remove(i) deletes the element at index i. You can import the class once with import scala.collection.mutable.ArrayBuffer and then write just ArrayBuffer(...).
Choosing between them
Use a List when the values should never change after they are built. This is the default in most Scala code, and it is safe to pass around. Use an Array when you know the size and want to overwrite elements in place. Use an ArrayBuffer when you will add or remove items while the program runs, as when collecting results one by one.
The three share the same reading syntax and many methods, including size, length, head and last, so switching from one to another usually changes only the line that creates the collection.
The exercise
The lesson wants two ArrayBuffer variables:
fruitsholding"mango","kiwi"and"grapefruit".agesholding14,32,50and21.
val fruits = scala.collection.mutable.ArrayBuffer("mango", "kiwi", "grapefruit")
val ages = scala.collection.mutable.ArrayBuffer(14, 32, 50, 21)
Both are declared with val. That only means the variable can't be pointed at another buffer. Appending to ages later is still allowed.
Gotchas
- Parentheses, not brackets.
ages(0)reads an element.ages[0]is an error. mutablein the name. PlainList,VectorandMapare available without an import and are immutable. The mutable versions live inscala.collection.mutable.- Printing. Lists and buffers print readably (
List(1, 2, 3),ArrayBuffer(1, 2, 3)), but a plainArraydoes not. UsemkStringfor arrays.
Getting the size
Each of the three reports how many elements it holds with size. length returns the same number. For a list of three items, numbers.size is 3. The last valid index is always one less than the size, a fact you will use constantly in the next lessons.
Key takeaways
Listis immutable,Arrayhas a fixed size, andArrayBuffercan change size.- Index with parentheses, starting at 0.
- Use
ArrayBuffer(...)fromscala.collection.mutablewhen the collection has to grow.
