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.

Leave a Comment

Your email address will not be published. Required fields are marked *