0

When the Size of the ArrayList is 2, and we perform the deletion operation using foreach loop, it prints the last element, no exception is thrown,

for example :

ArrayList<String> a= new ArrayList<String>(Arrays.asList("abc","xyz"));
    for(String i : a){
        a.remove(i);
    }
// a contains "xyz"

but when we increase the size of the arrayList more than 2, and perform the operation it shows concurrentModificationException. Why it is showing such behavior(it works for 2 elements but not for more than 2)?

moovon
  • 2,219
  • 1
  • 17
  • 15
  • Have you seen this? https://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re – Magd Kudama Apr 25 '18 at 17:45

3 Answers3

1

It is a quirk of the implementation.

The exception is thrown on a best-effort basis only, there is no guarantee it will be thrown.

To understand why, you need to understand how enhanced for loops are desugared:

for (String i : a) {
  a.remove(i);
}

Is compiled to something like:

Iterator<String> it = a.iterator();
while (it.hasNext()) {
  String i = a.next();
  a.remove(i);
}

Next, the iterator is implemented something like:

int idx = 0;

boolean hasNext() {
  return idx < a.size();
}

Object next() {
  checkForModification(); // throws exception if list has been modified.
  return a.get(idx++);
}

So, if the list has size 2 initially, execution of the loop proceeds thus:

  • Check hasNext(): idx == 0, and 0 < 2 is true, so it returns true.
  • Call next(): retrieve i = a.get(0), increment idx, so idx == 1.
  • Remove i from a. Now a.size() == 1.

The loop has executed once. Now it executes again:

  • Check hasNext(): idx == 1, and 1 < 1 is false, so it returns false.

So execution stops, no ConcurrentModificationException. The list still contains the initial second item.

If the list is any bigger than 2 initially, the loop executes a second time, (because idx == 1, and size() == initial size() - 1, so idx < initial size() - 1), so next() will be called again after the remove(). The list has been modified, so checkForModification() throws an exception.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
1

ArrayList is not thread safe and you can have this type of trouble while you are changing the structure of it and iterating.

You need to use an Iterator to safely remove from a collection while iterating over it.

ArrayList<String> a= new ArrayList<String>(Arrays.asList("abc","xyz"));
for (Iterator<String> iterator = a.iterator(); iterator.hasNext();) {
    String string = iterator.next(); // pick one element

    // do some stuffs with the content

    iterator.remove(); // remove element
}
Glim
  • 361
  • 1
  • 10
  • 1
    In this case, iteration and modification happen on the same thread - it has nothing to do with thread-safety. Also, this doesn't really answer the OP's question of why the exception is thrown after 2 elements. – Vasan Apr 25 '18 at 18:07
  • @Vasan “you can have this type of trouble while you are changing the structure of it and iterating”. Also, he doesn’t really asks why it occurs with 2 or more elements. He asks if he is getting an abnormal behavio (the short answer is no). – Glim Apr 25 '18 at 23:08
0

You cannot remove items from list while iterating it. Use iterator instead. Look over there:

Remove elements from collection while iterating

  • 1
    This doesn't answer the OP's actual question of why the exception is thrown after 2 elements. – Vasan Apr 25 '18 at 18:13