Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Iterate over a set while contents of set are being modified

I wish to iterate over a set but the contents of the set will modify during its iteration. I wish to iterate over the original set at the time the iterator was created and not iterate over any of the new elements added to the set. How is this possible? Is this is the default behavior of set or how can I accomplish this?

One way I can think of is to get a new set from the original set which won't be modified but this seems inelegant and there must be a better solution.

like image 262
nomel7 Avatar asked Jun 19 '12 18:06

nomel7


People also ask

Can we modify Collection while iterating Java?

It is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances.

Can we modify list while iterating?

Because you iterate over a copy of the list, you can modify the original list without damaging the iterator.

Can we access the elements of a set using while loop?

You 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.

Can you modify a Collection in Java using a for each loop What about when using an iterator?

In for-each loop, we can't modify collection, it will throw a ConcurrentModificationException on the other hand with iterator we can modify collection.


2 Answers

Taking a snapshot of the set sounds like exactly the right solution to me, if you want to make sure you don't see any new elements. There are some sets such as ConcurrentSkipListSet which will allow you to keep iterating, but I can't see any guarantees around behaviour of an iterator in terms of seeing new elements.

EDIT: CopyOnWriteArraySet has the requirements you need, but writes are expensive, which sounds like it's not appropriate for you.

Those are the only sets I can see in java.util.concurrent, which is the natural package for such collections. Taking a copy is still likely to be simpler :)

like image 82
Jon Skeet Avatar answered Nov 11 '22 11:11

Jon Skeet


EDIT: This answer was designed for a single-threaded case, since I had interpreted the OP's question as avoiding comodification rather than avoiding issues from multithreading. I'm leaving this answer here in case it ends up being useful to anyone in the future who is using a single-threaded approach.

There is no direct way to accomplish this. However, one option that is quite nice is to have two sets - the main set, which you iterate over, and a secondary set into which you insert all the new elements that need to be added. You can then iterate over the primary set, and then once that's finished go and use addAll to add all the new elements to the primary set.

For example:

Set<T> masterSet = /* ... */

Set<T> newElems = /* ... */
for (T obj: masterSet) {
     /* ... do something to each object ... */
}

masterSet.addAll(newElems);

Hope this helps!

like image 25
templatetypedef Avatar answered Nov 11 '22 10:11

templatetypedef