1

I have a for each loop with a Set type.

While I loop through this Set I add elements to it.

 for (Object o: Set) {
    //i do something and add to the set
  }

I keep getting the ConcurrentModificationException. But I don't know how I would go about adding to this Set that wouldn't cause this exception. I can't create a new for each loop that wouldn't work.

Can anyone point me in the right direction?

Pragmateek
  • 13,174
  • 9
  • 74
  • 108
hat_to_the_back
  • 99
  • 1
  • 12
  • 2
    @RuchiraGayanRanaweera: Iterators allow for *removal* of values, but not *addition* as far as I'm aware. – Jon Skeet Jul 11 '14 at 10:24
  • What do you mean by "I cant create a new for each loop that wouldnt work" - what exactly wouldn't work, and in what way wouldn't it work? I'd normally just build up a collection of "things to add afterwards" and add them afterwards... – Jon Skeet Jul 11 '14 at 10:25
  • What I mean is I need to use the exact same set and continuously update it. But I cant seem to figure out how to do it? – hat_to_the_back Jul 11 '14 at 10:28
  • 1
    @RuchiraGayanRanaweera: You think incorrectly. – Jon Skeet Jul 11 '14 at 10:35
  • What you're trying to do is generally impossible with the standard collections. However, if you could elaborate a bit more, there might be a specific solution to your problem. – Ray Jul 11 '14 at 10:38
  • A common way of doing this is to create a new set, and add to that while iterating over the original. – Chris K Jul 11 '14 at 10:40
  • I am collecting items into a set which I am concurrently iterating through. Basically it will keep collecting info until there is no more info then eventually the lopp will stop. But i have to do this in the same loop. ArrayLists worked just fine but Im gathering huge amounts of data? – hat_to_the_back Jul 11 '14 at 10:40

2 Answers2

2

You need to either

  • use a Set which doesn't trigger a ConcurrentModifcationException e.g. ConcurrentXxxSet
  • take a copy of the set before altering it.

The simplest change is

for (Object o: set.toArray()) {
    if (condition(o))
       set.add(something);
}

With generics a simpler solution might be

Set<T> set = 
for(T t: new HashSet<T>(set)) {
   // something which might add to set
}

Note: this will prevent it iterating over elements you just added. Note2: using the concurrent sets may result in you seeing added elements or possibly not.

If you really need to see elements as you add them you need to use a list, possibly instead, or as well.

List<T> list = new ArrayList<>(set);

for(int i = 0; i < list.size(); i++) {
    T t = list.get(i);

    if (condition)
        // if it is not a duplicate
        if (set.add(something))
            list.add(something);
}

This will allow you to see elements you have added in the loop in a reliable manner. Note: some care needs to be take to not create an infinite loop and run out of memory.

An alternative is to use a queue instead of a list. This might be nicer, but I suspect less efficient.

Queue<T> q = new ConcurrentLinkedQueue<>(set);

for(T t : q) {
    if (condition)
        // if it is not a duplicate
        if (set.add(something))
            q.add(something);
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

You could make a temporary Set and add your elements to this temp Set, and when you finished lopping over all elements, add the values from the temp Set to the original Set

Example:

Set<Integer> originalSet;
//stuff where you fill your original Set
Set<Integer> tempSet = new Set<Integer>();

for(Integer i : originalSet) {
    tempSet.add(<integerToAdd>);
}
originalSet.addAll(tempSet); 
bvanvelsen - ACA Group
  • 1,741
  • 1
  • 14
  • 25