List and Seq
Asha logs four runs this week, and the logbook needs to keep them in order. Scala's workhorse for an ordered group of values is List, and knowing what it does quickly, and what it does slowly, shapes how you use it.
The rule
A List never changes: add at the front, walk from the start.
val runs: List[Double] = List(5.0, 8.5, 3.2, 10.0)
runs.head // 5.0, the first element
runs.tail // List(8.5, 3.2, 10.0), everything after the head
runs(1) // 8.5: positions count from 0
runs.length // 4
runs.sum // 26.7The type parameter. List[Double] reads "a list of Doubles". The type in square brackets is a type parameter: List works for any element type, and the brackets say which one this list holds. List[String] holds text. Scala usually infers it, so List(5.0, 8.5) is already a List[Double].
Immutable. A List cannot be changed. Operations that look like changes return a new list and leave the original alone:
val withMonday = 6.0 :: runs // List(6.0, 5.0, 8.5, 3.2, 10.0)
val withSunday = runs :+ 7.0 // List(5.0, 8.5, 3.2, 10.0, 7.0)
runs.length // still 4A List is a chain in which each element points to the rest. Adding at the front with :: makes one new link pointing at the old chain, so it is fast whatever the length. Adding at the end with :+, or reading runs(1000), has to walk the chain from the start.
Nil is the empty list, and List.empty[Double] is an empty list of Doubles. Asking an empty list for its head throws NoSuchElementException; headOption returns None instead. Asking for a position past the end, like runs(10), throws IndexOutOfBoundsException.
Seq is the general type for any ordered sequence. A List is a Seq, and so is a Vector. Writing Seq(5.0, 8.5) gives you a List behind the scenes. A function that only needs "some ordered values" should accept a Seq, so callers can pass whichever kind they have.
The shortcut
Ask "which end am I working at?" Adding at the front, or walking from start to finish: a List is ideal. Frequent reads or updates in the middle: reach for a Vector, the next lesson.
Start
runsholds four runs.
Drills
Answer fast — trust the rule.
With runs = List(5.0, 8.5, 3.2, 10.0), what is runs(1)?
After val more = 6.0 :: runs, what is runs.length?
In List[String], what is [String]?
What does List.empty[Double].head do?
Which operation stays fast on a List of a million runs?
Remember
- A List is immutable and ordered; the brackets in
List[Double]hold its type parameter. - Ask which end you work at: the front and start-to-finish walks suit a List; accept a
Seqwhen any ordered values will do. - Trap: expecting
::or:+to change the original, and callingheadon an empty list.
