Removing a value
s.remove(x) takes the value x out of the set s. It returns true if the value was there. If it wasn't, nothing happens: there is no error, and the method returns false.
HashSet<Integer> nums = new HashSet<>(List.of(5, 2, 9));
System.out.println(nums.remove(2));
System.out.println(nums.remove(2));
The first call finds the 2 and prints true. The second finds nothing and prints false.
A trap with lists
On a set, remove always takes the value, because a set has no indexes. A List<Integer> is different. For a list, remove(2) with an int argument removes the item at index 2, not the value 2. With List<Integer> list = new ArrayList<>(List.of(5, 2, 9)), calling list.remove(2) removes the 9 and leaves [5, 2]. Keep that in mind when you switch between the two.
Looping over a set
A for-each loop visits each value of a set once:
HashSet<String> colors = new HashSet<>(List.of("red", "blue", "green"));
colors.remove("blue");
for (String color : colors) {
System.out.println(color);
}
This prints red and green, in no promised order. It may differ from the order you added the values in. Loop over a set when the order doesn't matter: a total, a count or a check are all fine.
Don't remove while looping
If you remove values from a set inside a loop over that same set, Java stops with a ConcurrentModificationException. Remove first, then loop.
The lesson's task
In a word game, the set found holds the words a player found: cat, tree, sun, qqq and moon. The words "qqq" and "zzz" aren't allowed, so both must be removed. The player never found "zzz", so that removal changes nothing. Then each word scores one point per letter, and the loop adds up the lengths.
found.remove("qqq");
found.remove("zzz");
System.out.println(found.size() + " words");
int score = 0;
for (String word : found) {
score += word.length();
}
System.out.println(score + " points");
Four words are left: cat, tree, sun and moon. Their lengths are 3, 4, 3 and 4. The output is:
4 words
14 points
The total is the same whatever order the loop visits the words in, which is why a sum is a safe thing to compute from a set.
The reason for removing first is that a loop over a set walks through it as it is at that moment. A removal in the middle changes the set under the loop, and Java reports that by throwing the exception on the loop's next step. If you need to drop some values based on a rule, collect them in a second collection during the loop and remove them after it, or remove them before you loop at all, as this task does.
Common mistakes
- Checking
containsbefore everyremove. It isn't needed: removing a missing value is harmless. - Printing the set to check the order. A set has no fixed order, so print sizes or totals instead.
- Removing inside the loop that is reading the same set.
Key takeaways
remove(x)deletes a value and returns whether it was there.- Removing a value that isn't in the set does nothing.
- A for-each loop visits each value once, in no fixed order.
- Finish removing before you start looping.
