An introduction to Scala’s collection classes (and methods)

This post is an excerpt from the Scala Cookbook. (This is the introduction to Chapter 10, Collections.)

The Scala collection classes are rich, deep, and differ significantly from the Java collections, all of which makes learning them a bit of a speed bump for developers coming to Scala from Java.

When a Java developer first comes to Scala, she might think, “Okay, I’ll use lists and arrays, right?” Well, not really. The Scala List class is very different from the Java List classes -- including the part where it’s immutable -- and although the Scala Array is an improvement on the Java array in most ways, it’s not even recommended as the “go to” sequential collection class.

Because there are many collections classes to choose from, and each of those classes offers many methods, a goal of this chapter (and the next) is to help guide you through this plethora of options to find the solutions you need. Recipes in these chapters will help you decide which collections to use in different situations, and also choose a method to solve a problem. To help with this, the methods that are common to all collections are shown in this chapter, and methods specific to collections like List, Array, Map, and Set are shown in Chapter 11.

Three important concepts

There are a few important concepts to know when working with the methods of the Scala collection classes:

  • What a predicate is
  • What an anonymous function is
  • Implied loops

A predicate is simply a method, function, or anonymous function that takes one or more parameters and returns a Boolean value. For instance, the following method returns true or false, so it’s a predicate:

def isEven (i: Int) = if (i % 2 == 0) true else false

That’s a simple concept, but you’ll hear the term so often when working with collection methods that it’s important to mention it.

The concept of an anonymous function is also important. They’re described in depth in Recipe 9.1, but here’s an example of the long form for an anonymous function:

(i: Int) => i % 2 == 0

As shown in Recipe 9.1, that long form can be reduced to the following shorter form:

_ % 2 == 0

That doesn’t look like much by itself, but when it’s combined with the filter method on a collection, it makes for a lot of power in just a little bit of code:

// create a list
scala> val list = List.range(1, 10)
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

// get only the even values from the list
scala> val evens = list.filter(_ % 2 == 0)
events: List[Int] = List(2, 4, 6, 8)

This is a nice lead-in into the third topic: implied loops. As you can see from that example, the filter method contains a loop that applies your function to every element in the collection and returns a new collection. You could live without the filter method and write equivalent code like this:

for {
    e <- list
    if e % 2 == 0
} yield e

but I think you’ll agree that the filter approach is both more concise and easier to read.

Collection methods like filter, foreach, map, reduceLeft, and many more have loops built into their algorithms. As a result, you’ll write far fewer loops when writing Scala code than with another language like Java.