2

I'm trying to remove elements from a set (someObjectSet) while looping through it. As I googled, using removeIf should avoid ConcurrentModificationException in this case. However this doesn't work for me.

Did google lie to me (or I misundertood it), or I'm not using removeIf correctly?

Set<SomeObject> someObjectSet = new HashSet<>();
someObjectSet.add(obj1);
someObjectSet.add(obj2);
someObjectSet.add(obj3);

for (SomeObject obj : someObjectSet ){
    ...
    someObjectSet.removeIf(ele -> if ele satisfies some condition)
}

The reason I want to do removeif inside the loop is that, in each loop, it can be determined that some other elements of the set no longer need to go in the loop, therefore I’m removing it so that the for loop won’t pick them up again.

For example,
In loop1, obj1 gets picked.
Then in the same loop it finds out obj2 no longer needs to be processed => remove obj2 from the set.
In loop2, instead of obj2, obj3 is picked up

Thanks in advance!

1 Answer 1

2

Don't iterate and removeIf using elements of your iteration. Beside the problem you're experiencing right now, those calls amount to iterating through the entire collection for each element of the collection (so you're still removing from the collection while iterating, which explains the exception!).

removeIf iterates for you, so all you need is a predicate of SomeObject:

//no loop
someObjectSet.removeIf(ele -> if ele satisfies some condition);

Where ele -> if ele satisfies some condition is the condition that each SomeObject element will be tested against (the ones passing the test will be removed). forEach will orchestrate the test on all elements in someObjectSet, you don't need to do that.


If you're having a secondary condition based on which you want to remove elements, then you can compose predicates (with or), something like in this example:

Set<Integer> set = new HashSet<>(Set.of(1, 2, 3, 4, 5, 6, 7, 8, 9));

Predicate<Integer> predicate = s -> s % 2 == 0;
Predicate<Integer> predicate2 = predicate.or(s -> s % 3 == 0);
set.removeIf(predicate2);

// Test with set.removeIf(predicate);
// then with set.removeIf(predicate2);
// and compare results
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks @ernest_k. The reason I want to do removeif inside the loop is that, in each loop, it can be determined that some other elements of the set no longer need to go in the loop, therefore I’m removing it so that the for loop won’t pick them up again. Do I have to use iterator for this purpose?
I see. Is it that the lambda expression you pass to removeIf cannot cover both of those conditions? That may make an interesting question, so please add a simple example...
I see what you mean Ernest. I think a more straightforward and simpler solution would be to maintain another toSkipSet and then in each loop, skip if obj is in the toSkipSet. Even iterator won’t help me in this case?
@wayne That is normally a good alternative to modifying a collection while iterating through it, and it should work. This answer assumed you want to use removeIf. Iterator would be able to achieve it, but your toSkipSet-based solution would clearly be better and simpler, in my opinion.
@wayne so to save some work in the outer iteration, you used removeIf on all elements, including those you’ve already processed… The solution is not to maintain a growing set of elements to skip, but a queue of element to process: Queue<SomeObject> queue = new ArrayDeque<>(someObjectSet); while(!queue.isEmpty()) { SomeObject obj = queue.remove(); /* ... */ queue.removeIf(ele -> /*if ele satisfies some condition*/); }
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.