Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove Elements from a HashSet while Iterating [duplicate]

People also ask

Can you remove from a Set while iterating?

Since we can't modify a set while iterating over it, we can create a duplicate set and remove elements that satisfy the condition from the original set by iterating over the duplicate set. The following code uses Java 8 Stream for filtering, but we can also use an iterator or a for-each loop.

How will you efficiently remove elements while iterating a collection?

The right way to remove objects from ArrayList while iterating over it is by using the Iterator's remove() method.

Can we iterate HashSet using for loop?

Iterating over Set using IteratorYou can use while or for loop along with hasNext(), which returns true if there are more elements in the Set. Call the next() method to obtain the next elements from Set.


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.


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();
    }
}

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-


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);