The Collections Library

17.1 Hierarchy (2.13+/Scala 3)

scala.collection.immutable — default (imported automatically).

scala.collection.mutable — ArrayBuffer, ListBuffer, HashMap, HashSet, Queue, Stack, ArrayDeque, PriorityQueue, StringBuilder.

Iterable
├── Seq
│   ├── IndexedSeq: Vector, ArraySeq, Range, String (via wrapper)
│   └── LinearSeq: List, LazyList, Queue
├── Set: HashSet, TreeSet (SortedSet), ListSet, BitSet
└── Map: HashMap, TreeMap (SortedMap), ListMap, VectorMap

17.2 List (immutable singly linked)

val xs = List(1, 2, 3)
0 :: xs                  // prepend O(1): List(0, 1, 2, 3)
xs :+ 4                  // append O(n)
xs ++ List(4, 5)
xs.head, xs.tail, xs.last, xs.init
xs.isEmpty, xs.length
Nil                      // empty list
List.fill(3)("x"), List.range(1, 5), List.tabulate(5)(i => i * i)

17.3 Vector (immutable, effectively O(1) indexed access & append)

Default choice for indexed sequences.

val v = Vector(1, 2, 3)
v(1), v.updated(1, 20), v :+ 4, 0 +: v

17.4 Set & Map

val s = Set(1, 2, 3)
s + 4, s - 1, s.contains(2), s(2)
s ++ Set(5), s & Set(2, 3), s | Set(9), s diff Set(1)

val m = Map("a" -> 1, "b" -> 2)
m("a")                    // 1 (throws NoSuchElementException if missing)
m.get("z")                // None
m.getOrElse("z", 0)
m + ("c" -> 3), m - "a", m ++ Map("d" -> 4)
m.updated("a", 10)
m.updatedWith("a")(_.map(_ + 1))
m.keys, m.values, m.toList
m.map((k, v) => k -> v * 2)
m.view.mapValues(_ * 2).toMap
m.filter((k, v) => v > 1)
m.withDefaultValue(0)("missing")    // 0

17.5 Arrays (mutable, JVM arrays)

val arr = Array(1, 2, 3)
arr(0) = 10
Array.ofDim[Int](3, 3)
arr.sorted, arr.toList, arr.mkString(", ")

17.6 Mutable collections

import scala.collection.mutable

val buf = mutable.ArrayBuffer(1, 2, 3)
buf += 4; buf ++= List(5, 6); buf -= 1; buf.insert(0, 0); buf.remove(0)

val counts = mutable.Map.empty[String, Int].withDefaultValue(0)
for w <- "a b a".split(" ") do counts(w) += 1

val pq = mutable.PriorityQueue(3, 1, 2)
pq.dequeue()   // 3

17.7 Common operations (work on all collections)

val nums = List(5, 3, 8, 1, 9, 2)

nums.map(_ * 2)
nums.filter(_ > 3), nums.filterNot(_ > 3)
nums.flatMap(n => List(n, n))
nums.foldLeft(0)(_ + _), nums.foldRight(0)(_ + _)
nums.reduce(_ max _), nums.reduceOption(_ + _)
nums.sum, nums.product, nums.min, nums.max, nums.maxOption
nums.sorted, nums.sortBy(-_), nums.sortWith(_ > _)
nums.reverse, nums.distinct
nums.take(2), nums.drop(2), nums.takeWhile(_ > 2), nums.dropWhile(_ > 2)
nums.takeRight(2), nums.slice(1, 3), nums.splitAt(2)
nums.find(_ > 5), nums.exists(_ > 8), nums.forall(_ > 0), nums.count(_ % 2 == 0)
nums.indexOf(8), nums.indexWhere(_ > 5), nums.contains(3)
nums.partition(_ % 2 == 0)              // (List(8, 2), List(5, 3, 1, 9))
nums.groupBy(_ % 3)                     // Map[Int, List[Int]]
nums.groupMapReduce(_ % 2)(identity)(_ + _)
nums.zip(nums.tail), nums.zipWithIndex, nums.unzip
nums.grouped(2).toList, nums.sliding(3).toList
nums.scanLeft(0)(_ + _)
nums.collect { case n if n > 4 => n * 10 }
nums.mkString("[", ", ", "]")
nums.headOption, nums.lastOption
nums.span(_ > 2), nums.tapEach(println)
nums.toSet, nums.toVector, nums.toArray, nums.iterator
List(List(1, 2), List(3)).flatten
List("a" -> 1, "b" -> 2).toMap
nums.dis
…

17.8 Ranges

1 to 10, 1 until 10, 1 to 10 by 2, 10 to 1 by -1, 'a' to 'e'
BigDecimal(0) to BigDecimal(1) by BigDecimal("0.1")   // Double ranges were removed in 2.13; use BigDecimal

17.9 Views & laziness

Views apply transformations lazily without intermediate collections.

(1 to 1_000_000).view.map(_ * 2).filter(_ % 3 == 0).take(5).toList

17.10 LazyList (lazy, memoized, possibly infinite)

val naturals: LazyList[Int] = LazyList.from(0)
val fibs: LazyList[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map(_ + _)
fibs.take(10).toList
LazyList.iterate(1)(_ * 2).takeWhile(_ < 1000).toList

17.11 Iterators

Single-pass; cheap for streaming over data.

val it = Iterator(1, 2, 3)
while it.hasNext do println(it.next())
Iterator.continually(readLine()).takeWhile(_ != "quit")

17.12 Performance characteristics

C = constant, eC = effectively constant, aC = amortized constant, L = linear.

17.13 Converting to/from Java collections

import scala.jdk.CollectionConverters.*
val jlist: java.util.List[Int] = List(1, 2).asJava
val slist: List[Int] = jlist.asScala.toList