# Immutable Collections — Scala

Source: https://www.geekswithgeeks.com/en/scala/c-collections

> Choose and use List, Vector, Map and Set with their transformation methods.

## A rich, immutable-first library

Scala's collections are **immutable by default**: operations return new collections and leave the original unchanged, which makes code easier to reason about and safe to share between threads. The main types are **`List`** (a singly linked list: fast prepend with `::` and head/tail access, slow indexed access), **`Vector`** (fast indexed access and updates, a good default sequence), **`Map`** (key-value pairs, created with `"pune" -> 2`), **`Set`** (unique elements), **`Range`** (`1 to 10`, `0 until n`) and **`Array`** (a mutable Java array). Mutable versions live in `scala.collection.mutable` (such as `ArrayBuffer` and `mutable.Map`) for local, performance-sensitive code. All share a large API: `map`, `filter`, `flatMap`, `foldLeft`, `reduce`, `sum`, `groupBy`, `groupMapReduce`, `partition`, `sortBy`, `zip`, `collect` (filter and map with a partial function), `find`, `exists`, `forall`, `take`, `drop`, `sliding` and `distinct`. **`.view`** creates lazy views that avoid intermediate collections, and **`LazyList`** handles potentially infinite sequences.

## Collection operations on orders

Grouping, folding and collecting without mutation.

```scala
case class Order(id: String, city: String, totalPaise: Long, paid: Boolean)

val orders = Vector(
  Order("o1", "Pune", 120000, paid = true),
  Order("o2", "Delhi", 30000, paid = false),
  Order("o3", "Pune", 80000, paid = true)
)

val revenue = orders.filter(_.paid).map(_.totalPaise).sum              // 200000
val byCity = orders.groupBy(_.city)                                    // Map[String, Vector[Order]]
val cityTotals = orders.groupMapReduce(_.city)(_.totalPaise)(_ + _)    // Map(Pune -> 200000, Delhi -> 30000)
val (unpaid, settled) = orders.partition(!_.paid)
val largest = orders.maxBy(_.totalPaise).id                            // o1
val bigIds = orders.collect { case Order(id, _, total, true) if total > 100000 => id }   // Vector(o1)

val runningTotals = orders.scanLeft(0L)(_ + _.totalPaise)              // Vector(0, 120000, 150000, 230000)

val cities = Set("Pune", "Delhi") + "Mumbai"                           // a new Set
val prepended = "x" :: List("y", "z")                                   // List(x, y, z)
val updated = cityTotals.updated("Mumbai", 0L)                          // original map unchanged

val firstEvenSquares = (1 to 1_000_000).view.map(n => n * n).filter(_ % 2 == 0).take(3).toList
```

## List is not always the right default

Indexing into a `List` with `list(i)` is O(n). For random access or appending, use `Vector` (or `ArrayBuffer` locally). Use `List` when you mostly prepend and process from the head, as in recursion.

**Quiz:** What happens when you call `updated` on an immutable Map?

- [ ] The original map is changed
- [x] A new map is returned and the original is unchanged
- [ ] An exception is thrown
- [ ] The map becomes mutable

*Answer:* A new map is returned and the original is unchanged. Immutable collection operations return new collections, sharing structure where possible.
