Choosing collections well matters for performance and memory in real workloads.
The collections library is a hierarchy: Iterable at the top, then Seq (ordered, with IndexedSeq like Vector/ArraySeq and LinearSeq like List), Set and Map. Code against the most general type that fits (Seq[A], Iterable[A]), and pick the concrete one for its performance:
List: O(1) prepend/head/tail, O(n) index and append.Vector: effectively constant index, update, append and prepend.ArraySeq/Array: compact and fastest for primitives;Arrayis mutable.HashMap/HashSetdefault;TreeMap/TreeSetkeep keys sorted;ListMapkeeps insertion order but is slow (the default immutableMapkeeps insertion order only up to 4 entries).
Chained operations like xs.map(f).filter(p).take(10) build an intermediate collection at each step. A view (xs.view.map(f).filter(p).take(10).toList) fuses them lazily and computes only what's needed. LazyList is a lazily evaluated, memoised sequence that can be infinite; Iterator is a one-shot lazy stream.
Mutable collections (scala.collection.mutable.ArrayBuffer, HashMap, ListBuffer) are fine for local, performance-sensitive building; keep them private and expose immutable results (buf.toList). The scala.jdk.CollectionConverters import adds .asScala/.asJava for Java interop.
Example
import scala.collection.mutable
val evens = LazyList.from(1).filter(_ % 2 == 0)
println(evens.take(5).toList) // List(2, 4, 6, 8, 10)
var calls = 0
val r = (1 to 1_000_000).view
.map { x => calls += 1; x * x }
.filter(_ % 7 == 0)
.take(3)
.toList
println(s"$r after $calls calls") // List(49, 196, 441) after 21 calls
val buf = mutable.ArrayBuffer.empty[Int]
for i <- 1 to 5 do buf += i * 10
val frozen: Vector[Int] = buf.toVector // expose immutable
println(frozen) // Vector(10, 20, 30, 40, 50)
val counts = mutable.Map.empty[Char, Int].withDefaultValue(0)
"mississippi".foreach(c => counts(c) += 1)
println(counts.toSeq.sortBy(-_._2).head) // (i,4) or (s,4)
val sorted = collection.immutable.TreeMap(3 -> "c", 1 -> "a", 2 -> "b")
println(sorted.keys.toList) // List(1, 2, 3)Remember
- Code against Seq/Iterable; choose List, Vector or ArraySeq for performance
- Views and LazyList avoid intermediate collections
- Mutable collections for local building, immutable results outward
- TreeMap for sorted keys; default Map order is not guaranteed
Watch out for
- Relying on the iteration order of a default immutable Map or Set with more than four elements.