Start learning free

LESSON 234 OF 677 · SETS

Sets in Scala: Mutable Sets Without Repeats

Lesson Read the full explanation

TL ; DR:

A mutable Set holds values without repeats. Adding a value that is already in it changes nothing.


val pets = scala.collection.mutable.Set[String]()    // an empty set
pets += "cat"
pets += "dog"
pets += "cat"                     // already in it, no change

println(pets.size)                // 2
println(pets.contains("dog"))     // true


Full lesson:

An ArrayBuffer keeps every value you put in it, repeats too. Sometimes you only need to know which values there are. A set is made for that.

A set holds each value once. Adding a value that is already in it changes nothing.

  • scala.collection.mutable.Set[String]() makes an empty set of strings.
  • s += x puts x in the set s.
  • s.contains(x) is true when x is in the set.
  • s.size gives how many values the set holds.
  • scala.collection.mutable.Set[String](buf.toSeq: _*) builds a set from the ArrayBuffer buf. Its repeats go in once.

The : _* hands the values to the set one by one. It takes an Array or a plain sequence. .toSeq turns the ArrayBuffer into one.

Checking s.contains(x) is fast, even for a big set. A list's contains looks at the values one by one.

Scala's plain Set can't change. += on a val of it doesn't compile. That's why we use scala.collection.mutable.Set.

A set has no order. A printed set may show its values in any order.


Your task

The camp's sign-up list, names, has some names twice. Build a mutable set named kids from it.

Then Zoe signs up, and Ava signs up again. Add both names to kids.

Last, if kids contains "Leo", print Leo is coming.

The code already prints the number of kids twice.

Expected output:

3 kids
4 kids
Leo is coming
STEP 1 OF 3
    Ctrl + Enter to check

    SCALA · SETS

    Sets in Scala, explained

    What a set is

    A buffer keeps every value you put in it, repeats included. A set keeps each value once. Adding a value that is already inside changes nothing, so a set is the right tool when you only care about which values exist.

    val pets = scala.collection.mutable.Set[String]()
    pets += "cat"
    pets += "dog"
    pets += "cat"
    
    println(pets.size)
    println(pets.contains("dog"))
    

    This prints 2 and true: the second "cat" was ignored.

    The operations

    • scala.collection.mutable.Set[String]() makes an empty set of strings.
    • s += x adds x.
    • s.contains(x) is true when x is in the set.
    • s.size is the number of values.

    Checking contains on a set is fast, even for a big set. A list has to look at its values one by one.

    Building a set from a buffer

    To drop the repeats of an ArrayBuffer, build a set from it:

    val names = scala.collection.mutable.ArrayBuffer("Ava", "Leo", "Ava", "Mia", "Leo")
    val kids = scala.collection.mutable.Set[String](names.toSeq: _*)
    

    The set constructor takes its values one by one, and : _* hands the elements of a sequence over that way. names.toSeq turns the buffer into a plain sequence first. Ava and Leo are each in the buffer twice, and the set holds each once, so kids.size is 3.

    Mutable versus immutable

    Scala's plain Set cannot change. A val holding one does not accept +=, and the code does not compile. Import or write out scala.collection.mutable.Set when you want a set that grows, as in this lesson.

    If you only want the number of different values, you don't need a mutable set: for the buffer above, names.size is 5 and names.toSet.size is 3. Here the plain, unchangeable Set is fine, because nothing is added afterwards.

    Trying += on a plain Set held in a val gives the compiler error value += is not a member of Set[String].

    No order

    A set has no order. If you print one, its values can come out in any order, so compare sizes and use contains instead of relying on position.

    The exercise

    The camp's sign-up list names has some names twice. The task is to build a set kids from it, add Zoe and then Ava, and print Leo is coming if Leo is in the set. The code already prints the size twice.

    class Solution {
        def mainFunction(): Unit = {
            val names = scala.collection.mutable.ArrayBuffer("Ava", "Leo", "Ava", "Mia", "Leo")
    
            val kids = scala.collection.mutable.Set[String](names.toSeq: _*)
            println(s"${kids.size} kids")
    
            kids += "Zoe"
            kids += "Ava"
            println(s"${kids.size} kids")
    
            if (kids.contains("Leo")) {
                println("Leo is coming")
            }
        }
    }
    

    The output is:

    3 kids
    4 kids
    Leo is coming
    

    The first count is 3 because Ava and Leo collapsed to one copy each. Adding Zoe makes it 4. Adding Ava again changes nothing.

    Common mistakes

    • Expecting += to report whether the value was new. It just adds. (The method add returns true for a new value and false for a repeat, if you need that.)
    • Using the plain Set and wondering why += fails.
    • Writing var kids out of habit. The name kids always refers to the same set and the set itself grows, so val is right.
    • Expecting the size to go up when you add a value that is already there. Ava's second sign-up leaves the count where it was.
    • Counting a buffer's size when you want the number of different values.

    Key takeaways

    • A set holds each value once and has no order.
    • += adds, contains checks, size counts.
    • Build one from a buffer with Set[T](buf.toSeq: _*) to remove repeats.
    • Use scala.collection.mutable.Set for a set you change.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free