Getting a ConcurrentModificationException thrown when removing an element from a java.util.List during list iteration?
I believe this is the purpose behind the Iterator.remove() method, to be able to remove an element from the collection while iterating.
For example:
Iterator<String> iter = li.iterator();
while(iter.hasNext()){
if(iter.next().equalsIgnoreCase("str3"))
iter.remove();
}
ConcurrentModificationException while trying to delete an item from ArrayList
You cannot call remove()
from inside a "for-each" loop (the for (item : collection)
structure). Doing so will throw that ConcurrentModificationException
.
If you want to loop and remove items while looping, you can use a traditional "for" loop:
for (int i = 0; i < itemStorico.size(); ++i) {
ItemModel itemModel2 = itemStorico.get(i);
if (...) {
itemStorico.remove(i);
...
}
}
However, this will introduce a subtle bug. Imagine you're looping over [a,b,c,d]
. If you are at index 1 and remove b
from the collection, everything will "slide over" and you'll have [a,c,d]
. When you next look at index 2, you'll have skipped over c
.
You can avoid the bug by manually decrementing the index when you remove, but it's a little gross.
...
itemStorico.remove(i);
--i;
...
You can also avoid the problem altogether by using the collection's iterator()
.
for (Iterator<ItemModel> iterator = itemStorico.iterator(); iterator.hasNext(); ) {
ItemModel itemModel2 = iterator.next();
if (...) {
iterator.remove();
...
}
}
How to avoid ConcurrentModificationException while removing elements from `ArrayList` while iterating it?
Use an Iterator
and call remove()
:
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
ConcurrentModificationException when removing an object from ArrayList
While iterating the Arraylist
, you are not allowed to remove any item. If you want to remove an item while iteration, you can add removed item into newly created ArrayList
say,. willBeRemovedList
after iteration completed you can remove all of them at a time.
How to avoid java.util.ConcurrentModificationException when iterating through and removing elements from an ArrayList
Two options:
- Create a list of values you wish to remove, adding to that list within the loop, then call
originalList.removeAll(valuesToRemove)
at the end - Use the
remove()
method on the iterator itself. Note that this means you can't use the enhanced for loop.
As an example of the second option, removing any strings with a length greater than 5 from a list:
List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String value = iterator.next();
if (value.length() > 5) {
iterator.remove();
}
}
ConcurrentModificationException: Why does removing the null in List throw this Exception if it´s not the first variable
Well, in a best case scenario, the ConcurrentModificationException
should be thrown in any of the two cases. But it isn't (see the quote from the docs below).
If you use the for-each loop to iterate over an Iterable
, the Iterator
returned by the iterator()
method of the data structure will be used internally (the for-each loop is just syntactic sugar).
Now you shouldn't (structurally) modify an Iterable
that is being iterated after this Iterator
instance was created (it's illegal unless you use the Iterator
's remove()
method). This is what a concurrent modification is: There are two different perspectives on the same data structure. If it's modified from one perspective (list.remove(object)
), the other perspective (the Iterator) won't be aware of this.
It is not about the element being null
. The same happens if you change the code to remove the string:
ArrayList<Object> s = new ArrayList<>();
s.add("test");
s.add(null);
try {
System.out.println(s + "\n");
for (Object t : s) {
System.out.println(t);
if (t != null && t.equals("test")) {
s.remove(t);
System.out.println("ObjectRemoved = " + t + "\n");
}
}
System.out.println(s + "\n");
} catch (ConcurrentModificationException e) {
System.out.println(e);
} catch (Exception e) {
System.out.println(e);
}
Now the reason this behaviour differs in some scenarios is simply the following (from the Java SE 11 Docs for ArrayList):
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
java.util.ConcurrentModificationException thrown when adding to List
The ConcurrentModificationException
is thrown when calling String value = it.next();
. But the actual culprit is list.add("6");
. You mustn't modify a Collection
while iterating over it directly. You are using it.remove();
which is fine, but not list.add("6");
.
While you can solve the problem with Stream
s, I will first offer a solution with Iterator
s, as this is a good first step for understanding the problem.
You need a ListIterator<String>
if you want to add and remove during iteration:
for (ListIterator<String> it = list.listIterator(); it.hasNext();){
String value = it.next();
if (value.equals("4")) {
it.remove();
it.add("6");
}
System.out.println("List Value: " + value);
}
This should do the trick!
A solution using Stream
s:
List<String> newList = list.stream()
.map(s -> s.equals("4") ? "6" : s)
.collect(Collectors.toList());
Here we create a Stream
from your List
. We map
all values to themselves, only "4"
gets mapped to "6"
and then we collect it back into a List
. But caution, newList
is immutable!
For java List concurrent modification exception not thrown if remove method invoked before iteration
Look at ArrayList.Itr
.
There're two counters: ArrayList.modCount
and ArrayList.Itr.expectedModCount
. ArrayList.Itr
throws ConcurrentModificationException
when modCount != expectedModCount
.
This is correct. But if you look at ArrayList.Itr.hasNext
you can see:
public boolean hasNext() {
return cursor != size;
}
When you remove one element and you list become empty, then your code does not invoke it.next()
.
To make your code throw ConcurrentModificationException
, you should have not empty list before while loop:
public static void main(String[] args)
{
List<String> l1 = new ArrayList<>();
l1.add("v1");
l1.add("v2");
Iterator<String> it = l1.iterator();
l1.remove(0);
while(it.hasNext()) {
System.out.println(it.next()); // --> ConcurrentModificationException
}
}
Why I'm not getting ConcurrentModificationException while removing element from ArrayList during iteration
The implementation makes a best effort to detect concurrent modification, but there are cases where it fails to do so.
The Iterator
implementation returned for ArrayList
's Iterator
checks for concurrent modification in next()
and remove()
, but not in hasNext()
, whose logic is:
public boolean hasNext() {
return cursor != size;
}
Since you removed an element when the Iterator
's cursor was at the element before the last element, the removal causes hasNext()
to return false
(since size
becomes equal to cursor
after the removal), which ends your loop without throwing an exception.
java.util.ConcurrentModificationException when removing elements from a hashmap
Your for-loop gets the map.entrySet and uses the iterator on it to work through the map entries (this version of the for-loop requires an Iterable, it gets the iterator from the Iterable). When you are using an iterator on a map, but remove things from the map without using that iterator, you get the ConcurrentModificationException. That is the map telling the iterator that it's out of date.
You can write a for loop using the iterator explicitly, like this:
for (Iterator<Map.Entry<String, Integer> it = map.entrySet().iterator();
it.hasNext();) {
and use the iterator's remove method when you need to delete an entry.
Related Topics
How to Update a Textview of an Activity from Another Class
How to Find the Currently Running Applications Programmatically in Android
How to Check Certificate Name and Alias in Keystore Files
Android-Java- How to Sort a List of Objects by a Certain Value Within the Object
Webview and Cookies on Android
Android: Sending Data >20 Bytes by Ble
How to Add Image in a Textview Text
What Is Shareduserid in Android, and How Is It Used
Android Studio Debugger Highlights the Wrong Lines
How to Randomize Two Arraylists in the Same Fashion
How to Run Certain Task Every Day at a Particular Time Using Scheduledexecutorservice
Mod in Java Produces Negative Numbers
How to Read System Environment Variable in Spring Applicationcontext
Jpql Create New Object in Select Statement - Avoid or Embrace