3

Is it possible to overwrite some HashSet element (not necessarily the one iterated) while iterating over it. I want to know of a way apart from removing, editing and then re-adding it? The reason I am asking this is because using remove while iterating always gives the java.util.ConcurrentModificationException. I figured out that there is no method to overwrite a set element.

Any help will be appreciated.

Thanks,

Somnath

3
  • It is a violation of the contract of a HashSet to modify the parts of a member of the Set that participate in the hashCode() or equals() methods. In general I recommend always deleting the old element and inserting the new element. Commented Mar 25, 2012 at 5:53
  • But deleting while iterating will give the exception. Commented Mar 25, 2012 at 5:55
  • See below for how to delete while iterating. Commented Mar 25, 2012 at 6:00

4 Answers 4

4

You can remove an item from a Collection - including a Set - whilst iterating over it as long as you iterate over it using an Iterator and call Itertator.remove() to do the remove. However, this will only allow you to remove the current item in the iteration.

You can't remove another item or add one as this will cause a ConcurrentModificationException, since if you change the Set it's not clear how you would iterate over it.

Also, unlike a List, is doesn't quite make sense to talk about replacing an entry in a Set. In a List items have a define order so, for example, if the second entry was "A" you could replace it with "B". Items in a Set don't have an order so you can't replace one with another directly, all you can do is remove the old one and add the new one.

Depending quite on what you want to do, your best approach might be to loop over a copy of the Set:

Set<Object> originalSet = someMethod(); 

for (Object item : new HashSet<Object>(originalSet)) {
  //do stuff which modifies originalSet
}

However, you'd have to account for the fact that the objects you iterated over would be the original values and wouldn't reflect any changes you'd made.

If this won't do, then it might make sense to find another way of process the items in the Set without simply iterating over them by keeping track of which nodes you've processed.

Something like this might do but could probably be improved depending on what you're doing:

Set<Object> originalSet = somemethod();

//Keep track of items we've already processed
Set<Object> processed = new HashSet<Object>();

//toDo is used to calculate which items in originalSet aren't in processed
Set<Object> toDo = new HashSet(originalSet);
toDo.removeAll(processed);

while (! toDo.isEmpty()) {
  //Get an object from toDo
  Object item = toDo.iterator().next();

  //Do some processing
  //Can update originalSet or even remove from processed if we want to re-do an item

  //Recalculate what's left to do
  processed.add(item);
  toDo = new HashSet(originalSet);
  toDo.removeAll(processed);
}
Sign up to request clarification or add additional context in comments.

1 Comment

I am using a set of graph nodes and the nodes have id and an attribute connected Component number. As I iterate over the graph nodes and found a certain node in the adjacency list of the iterated node, I need to update the connected component num. So, apparently it seems that I need to update the set I am iterating over. But I will try to see if that can be done on a copy of the original set.
2
Set<Key> set = Sets.newHashSet();
// ...

for (Key k : ImmutableSet.copyOf(set)) {
  if (needToMessWith(k)) {
    set.remove(k);
    k.mutateAsNeeded();
    set.add(k);
  }
}

// I used the guava collections code. It is simple enough to do without.

2 Comments

But calling the remove will give the java.util.ConcurrentModificationException. That's what I am saying.
Sorry, I added the copyOf() to shallow clone the set.
1

Just convert set to List and use set() and once you are done with this convert it back to Set

or maintain another Set while iterating

1 Comment

My reqmnt is such that I am traversing a set of nodes in a graph and have to update the node attribute - connected component number as I find the adjacencies and have to modify the set as I traverse.
1

There is no way to do that. Because replacing an element is basically removing + inserting. This is a set, not a list. For example, there are two elements in a set {1, 2} and you want to replace {1} with {2}. This is not possible as the result set cannot contain {2, 2} - i.e. two elements which are equal.

2 Comments

If the set is {1,2}, I want to update 2 while 1 is iterated. I consider 1, 2 as Objects with attributes in them
I suggest you to keep two sets: removed and added. And during iteration add ones for removal to the first one, and new entries to the latter. After the iteration block just "removeAll" and "addAll" correspondingly.

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.