Question

So, if I try to remove elements from a Java HashSet while iterating, I get a ConcurrentModificationException. What is the best way to remove a subset of the elements from a HashSet as in the following example?

Set<Integer> set = new HashSet<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

// Throws ConcurrentModificationException
for(Integer element : set)
    if(element % 2 == 0)
        set.remove(element);

Here is a solution, but I don't think it's very elegant:

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>();

for(int i = 0; i < 10; i++)
    set.add(i);

for(Integer element : set)
    if(element % 2 == 0)
        removeCandidates.add(element);

set.removeAll(removeCandidates);

Thanks!

Was it helpful?

Solution

You can manually iterate over the elements of the set:

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    if (element % 2 == 0) {
        iterator.remove();
    }
}

You will often see this pattern using a for loop rather than a while loop:

for (Iterator<Integer> i = set.iterator(); i.hasNext();) {
    Integer element = i.next();
    if (element % 2 == 0) {
        i.remove();
    }
}

As people have pointed out, using a for loop is preferred because it keeps the iterator variable (i in this case) confined to a smaller scope.

OTHER TIPS

The reason you get a ConcurrentModificationException is because an entry is removed via Set.remove() as opposed to Iterator.remove(). If an entry is removed via Set.remove() while an iteration is being done, you will get a ConcurrentModificationException. On the other hand, removal of entries via Iterator.remove() while iteration is supported in this case.

The new for loop is nice, but unfortunately it does not work in this case, because you can't use the Iterator reference.

If you need to remove an entry while iteration, you need to use the long form that uses the Iterator directly.

for (Iterator<Integer> it = set.iterator(); it.hasNext();) {
    Integer element = it.next();
    if (element % 2 == 0) {
        it.remove();
    }
}

you can also refactor your solution removing the first loop:

Set<Integer> set = new HashSet<Integer>();
Collection<Integer> removeCandidates = new LinkedList<Integer>(set);

for(Integer element : set)
   if(element % 2 == 0)
       removeCandidates.add(element);

set.removeAll(removeCandidates);

Java 8 Collection has a nice method called removeIf that makes things easier and safer. From the API docs:

default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate. 
Errors or runtime exceptions thrown during iteration or by the predicate 
are relayed to the caller.

Interesting note:

The default implementation traverses all elements of the collection using its iterator(). 
Each matching element is removed using Iterator.remove().

From: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-

Like timber said - "Java 8 Collection has a nice method called removeIf that makes things easier and safer"

Here is the code that solve your problem:

set.removeIf((Integer element) -> {
    return (element % 2 == 0);
});

Now your set contains only odd values.

Does it need to be whilst iterating? If all you're doing is filtering or selecting I would suggest using Apache Commons CollectionUtils. There are some powerful tools there and it makes your code "cooler."

Here's an implementation that should provide what you need:

Set<Integer> myIntegerSet = new HashSet<Integer>();
// Integers loaded here
CollectionUtils.filter( myIntegerSet, new Predicate() {
                              public boolean evaluate(Object input) {
                                  return (((Integer) input) % 2 == 0);
                              }});

If you find yourself using the same kind of predicate frequently you can pull that out into a static variable for reuse... name it something like EVEN_NUMBER_PREDICATE. Some may see that code and declare it "hard to read" but it looks cleaner when you pull out the Predicate into a static. Then it's easy to see that we're doing a CollectionUtils.filter(...) and that seems more readable (to me) than a bunch of loops all over creation.

An other possible solution:

for(Object it : set.toArray()) { /* Create a copy */
    Integer element = (Integer)it;
    if(element % 2 == 0)
        set.remove(element);
}

Or:

Integer[] copy = new Integer[set.size()];
set.toArray(copy);

for(Integer element : copy) {
    if(element % 2 == 0)
        set.remove(element);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top