Scala Lists
Lists are the most commonly used collection in Scala. A Scala List is an ordered, immutable sequence of elements of the same type. Once created, you cannot add or remove elements — instead, you create new lists based on old ones. This immutability makes Lists safe to share across threads and easy to reason about.
Creating a List
val fruits = List("Apple", "Banana", "Cherry")
val numbers = List(1, 2, 3, 4, 5)
val empty = List() // or List.empty[Int]
val mixed = List(1, "two", 3.0) // List[Any] — usually avoid this
The type is inferred: List("a", "b") produces a List[String] automatically.
How Lists Are Stored (Linked List)
List(1, 2, 3) internally looks like:
[1] ──→ [2] ──→ [3] ──→ Nil
Each element points to the next.
The last element points to Nil (empty list).
Think of a List like a chain of train wagons. The first wagon is the head, and each wagon links to the next. Adding to the front (prepending) is instant — you just attach a new front wagon. Adding to the end requires building a whole new chain.
Accessing Elements
val colors = List("Red", "Green", "Blue", "Yellow")
colors.head // "Red" — first element
colors.tail // List(Green, Blue, Yellow) — everything after head
colors(2) // "Blue" — element at index 2 (0-based)
colors.last // "Yellow" — last element
colors.length // 4
colors.isEmpty // false
colors.nonEmpty // true
Use headOption and lastOption instead of head and last to avoid exceptions on empty lists:
val safe = List.empty[String].headOption // None (not an exception)
val hasValue = List("a", "b").headOption // Some("a")
Prepending with ::
The :: operator (called "cons") prepends an element to a list. It creates a new list — the original is unchanged.
val base = List(2, 3, 4)
val extended = 1 :: base // List(1, 2, 3, 4)
val moreExtended = 0 :: extended // List(0, 1, 2, 3, 4)
println(base) // List(2, 3, 4) — unchanged
println(extended) // List(1, 2, 3, 4)
Concatenating Lists with ++
val list1 = List(1, 2, 3)
val list2 = List(4, 5, 6)
val combined = list1 ++ list2 // List(1, 2, 3, 4, 5, 6)
// Also works with ::: operator
val combined2 = list1 ::: list2 // same result
Key List Operations
map — Transform Every Element
val prices = List(100, 200, 300)
val discounted = prices.map(p => p * 0.9)
println(discounted) // List(90.0, 180.0, 270.0)
val names = List("alice", "bob", "carol")
val capitalized = names.map(_.capitalize)
println(capitalized) // List(Alice, Bob, Carol)
filter — Keep Elements That Pass a Test
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val evens = numbers.filter(_ % 2 == 0)
println(evens) // List(2, 4, 6, 8, 10)
val words = List("cat", "elephant", "fox", "hippopotamus")
val longWords = words.filter(_.length > 4)
println(longWords) // List(elephant, hippopotamus)
foldLeft — Accumulate a Result
val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.foldLeft(0)(_ + _)
println(sum) // 15
val product = numbers.foldLeft(1)(_ * _)
println(product) // 120
foldLeft(0)(_ + _) on List(1, 2, 3, 4, 5):
Start: acc = 0
Step 1: acc = 0 + 1 = 1
Step 2: acc = 1 + 2 = 3
Step 3: acc = 3 + 3 = 6
Step 4: acc = 6 + 4 = 10
Step 5: acc = 10 + 5 = 15
Result: 15
flatMap — Map Then Flatten
val sentences = List("Hello World", "Scala is fun")
val words = sentences.flatMap(_.split(" "))
println(words) // List(Hello, World, Scala, is, fun)
foreach — Perform an Action on Each Element
val students = List("Aarav", "Diya", "Rohan")
students.foreach(name => println(s"Hello, $name!"))
// Hello, Aarav!
// Hello, Diya!
// Hello, Rohan!
Searching and Checking
val scores = List(85, 92, 78, 95, 88)
scores.contains(92) // true
scores.exists(_ > 90) // true (any element > 90?)
scores.forall(_ > 70) // true (all elements > 70?)
scores.find(_ > 90) // Some(92) (first match)
scores.count(_ > 85) // 3
scores.max // 95
scores.min // 78
scores.sum // 438
scores.sorted // List(78, 85, 88, 92, 95)
scores.reverse // List(88, 95, 78, 92, 85)
Slicing and Transforming
val data = List(10, 20, 30, 40, 50)
data.take(3) // List(10, 20, 30)
data.drop(2) // List(30, 40, 50)
data.slice(1, 4) // List(20, 30, 40)
data.splitAt(2) // (List(10, 20), List(30, 40, 50))
data.zip(data.map(_ * 2)) // List((10,20),(20,40),(30,60),(40,80),(50,100))
Grouping and Partitioning
val numbers = List(1, 2, 3, 4, 5, 6)
val (evens, odds) = numbers.partition(_ % 2 == 0)
println(evens) // List(2, 4, 6)
println(odds) // List(1, 3, 5)
val grouped = numbers.groupBy(_ % 3)
println(grouped)
// Map(0 -> List(3, 6), 1 -> List(1, 4), 2 -> List(2, 5))
Sorting
case class Student(name: String, grade: Int)
val students = List(
Student("Charlie", 85),
Student("Alice", 92),
Student("Bob", 78)
)
// Sort by grade ascending
val byGrade = students.sortBy(_.grade)
byGrade.foreach(s => println(s"${s.name}: ${s.grade}"))
// Bob: 78
// Charlie: 85
// Alice: 92
// Sort by name alphabetically
val byName = students.sortBy(_.name)
byName.foreach(s => println(s.name))
// Alice, Bob, Charlie
mkString — Joining Elements
val words = List("Scala", "is", "powerful")
println(words.mkString(" ")) // Scala is powerful
println(words.mkString(", ")) // Scala, is, powerful
println(words.mkString("[", ", ", "]")) // [Scala, is, powerful]
Building a List with Ranges
val oneToTen = (1 to 10).toList
println(oneToTen) // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val evens = (2 to 20 by 2).toList
println(evens) // List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
// Build a list with List.fill
val zeros = List.fill(5)(0)
println(zeros) // List(0, 0, 0, 0, 0)
// Build a list with tabulate
val squares = List.tabulate(6)(n => n * n)
println(squares) // List(0, 1, 4, 9, 16, 25)
List vs Array vs Vector
Collection Immutable? Fast prepend? Fast random access? Best for
─────────── ────────── ──────────── ────────────────── ──────────────────
List Yes Yes (O(1)) No (O(n)) Sequential processing
Array No No Yes (O(1)) Fixed-size, mutable
Vector Yes Yes (O(log n)) Yes (O(log n)) Large collections
Lists are perfect for functional programming patterns like recursion, map, filter, and fold. Use Vector when you need fast random access with immutability. Use Array only when you need mutable, index-based access for performance-critical code.
