What a set is
A Set is a collection of unique values. Adding a value that is already present changes nothing, so a value is either in the set or not, and it can't appear twice. A hash set stores its values in a hash table, which makes "is this value in the set?" a constant-time operation on average. That fast membership check is the reason to reach for a set: whenever a loop keeps asking "have I seen this before?", a set answers without scanning a list.
Creating and reading sets
In Scala, Set(...) builds an immutable set. Duplicates in the arguments collapse into one element:
val mySet = Set("b", "c", "c", "d")
println(mySet) // Set(b, c, d)
println(mySet.contains("c")) // true
println(mySet.size) // 3
Besides contains, you can apply a set to a value like a function: mySet("c") is the same test and returns a Boolean. Two sets are equal when they hold the same elements, whatever the order: Set(1, 2) == Set(2, 1) is true.
Immutable and mutable sets
The default Set never changes. The + operator returns a new set and leaves the original untouched:
val im = Set(1, 2, 3)
println(im + 4) // Set(1, 2, 3, 4)
println(im) // Set(1, 2, 3)
To update a set in place, use the one from scala.collection.mutable. The package name is written out in full because the lesson doesn't import it:
val mutableSet = scala.collection.mutable.Set("b", "c", "d")
mutableSet += "x" // adds x
mutableSet -= "c" // removes c
The variable is a val, yet the set changes. A val only stops the variable from pointing at another object; the object it points to can still be modified when its type is mutable. += adds an element, -= removes one, and removing a value that isn't there does nothing. The method add also inserts, and returns true if the value was new and false if it was already present.
Iteration order
A hash set doesn't promise any particular order when you loop over it. If you want the elements back in the order they were first inserted, use scala.collection.mutable.LinkedHashSet. It is a hash set that also remembers insertion order, so printing or looping over it is predictable:
val ordered = scala.collection.mutable.LinkedHashSet("b", "a", "b", "c")
println(ordered) // LinkedHashSet(b, a, c)
The lesson's exercise, step by step
The exercise starts with data that contains a duplicate and then exercises each basic set operation:
- An array with a duplicate.
fruitsArrayholds"apples","bananas"and"apples". It is declared withvalandArray(...). - Build the set from the array. The constructor of
LinkedHashSet[String]takes the elements as separate arguments, so the array is spread withfruitsArray: _*. The duplicate"apples"disappears, and the order is apples, bananas. - Add a value.
fruits += "oranges"inserts it in place. Adding it a second time would have no effect. - Test membership.
fruits.contains("apples")istrue, so the program printsWe have apples. - Remove a value.
fruits -= "bananas"takes it out. - Loop over what is left. A
forcomprehension with a variablefruitprints each remaining element.
The solution in Scala
val fruitsArray = Array("apples", "bananas", "apples")
val fruits = scala.collection.mutable.LinkedHashSet[String](fruitsArray: _*)
fruits += "oranges"
if (fruits.contains("apples")) {
println("We have apples")
}
fruits -= "bananas"
for (fruit <- fruits) {
println(fruit)
}
The program prints:
We have apples
apples
oranges
The syntax fruitsArray: _* is the older way to pass an array as a sequence of arguments. Scala 3.5 still accepts it but reports a warning and prefers fruitsArray*. Both mean the same.
Common mistakes
- Expecting
+=to change an immutable set. On avalholding an immutableSet,+=doesn't compile, because there is no variable to reassign. Use the mutable set, or avarand accept that each addition builds a new set. - Relying on order from a plain hash set. The order of
Setandmutable.HashSetcan differ from the order you inserted. UseLinkedHashSetwhen the order matters. - Counting an array as a set.
Array(1, 2) == Array(1, 2)isfalse, because arrays compare by reference. Sets compare by content.
Key takeaways
- A set keeps one copy of each value and answers
containsin constant time on average. Set(...)is immutable;scala.collection.mutable.Setchanges in place with+=,-=andadd.- A
valcan hold a mutable set that still changes. LinkedHashSetadds insertion order to a hash set.
