Concurrent Modification Exception : adding to an ArrayList
ConcurrentModificationException occurs when you modify the list (by adding or removing elements) while traversing a list with Iterator
.
Try
List<Element> thingsToBeAdd = new ArrayList<Element>();
for(Iterator<Element> it = mElements.iterator(); it.hasNext();) {
Element element = it.next();
if(...) {
//irrelevant stuff..
if(element.cFlag){
// mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
thingsToBeAdd.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
element.cFlag = false;
}
}
}
mElements.addAll(thingsToBeAdd );
Also you should consider enhanced for each loop as Jon suggested.
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!
Throwing ConcurrentModificationException if we add elements in ArrayList at the end while iterating
There is probably a better solution, but this one is working as well. A second list is used and then added to the original one, after the loop has finished.
Main
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> al = new ArrayList<Integer>();
List<Integer> toAdd = new ArrayList<Integer>();
int stepSize = 10;
al.add(20);
al.add(30);
al.add(40);
Iterator<Integer> it = al.iterator();
while (it.hasNext()) {
int value = it.next();
if (value == al.get(al.size() - 1)){
toAdd.add(value + stepSize);
}
}
// Add all elements
al.addAll(toAdd);
// Print
al.forEach(System.out::println);
}
}
In case you only want to append one element based on the last value, as in your example, you could also wait until the loop is finished (if you still need it) and try to add it like this:
al.add(al.get(al.size() - 1) + stepSize);
Output
20
30
40
50
Java ConcurrentModificationException with an ArrayList
This code has multiple flaws. In this form it is going to add new Player instances until an entry with matching name is found. Starting an iteration, modifying a list, and continuing the iteration will throw a ConcurrentModificationException
as you observed. Furthermore you compare strings with ==
instead of equals
.
for (Player p : inGamePlayers) {
if (newUsername == p.username) { // This needs to be .equals
System.out.println(p.username+" reconnected!");
break;
}
// this line runs many times
inGamePlayers.add(new Player(newUsername));
}
Instead I suggest you to extract that code to a new function, and change the control flow:
private static void handleConnected(ArrayList<Player> inGamePlayers, String newUsername) {
for (Player p : inGamePlayers) {
if (newUsername.equals(p.username)) {
System.out.println(p.username+" reconnected!");
return; // return instead of break
}
}
// we did not return, so this user is new
inGamePlayers.add(new Player(newUsername));
}
// ...
public static void main(String[] args) {
ArrayList<Player> inGamePlayers = new ArrayList<Player>();
inGamePlayers.add(new Player("Griff"));
String newUsername = "Polak";
// Call this function in place of the old loop
handleConnected(inGamePlayers, newUsername);
for (Player p : inGamePlayers) {
System.out.println(p.username);
}
}
How to fix ConcurrentModificationException error when adding items during iteration
ListIterator has the add method, you can use it like this:
ListIterator<User> iterator = users.listIterator();
while(iterator.hasNext()) {
User u = iterator.next();
if(!user.getID().equals(u.getID())) {
iterator.add(user);
}
}
But to add a user only if it doesn't exist in the list you don't need an iterator. If you do it inside of the loop, you will add a user for each of the list's users that doesn't have the same id. But instead you need to check that the user's id is dirrenet from all users' ids and add the user only once.
If you are using Java 8, you could do something like this:
boolean userExists = users.stream().filter(u -> u.getID().equals(user.getID())).findFirst().isPresent();
if (!userExists) {
users.add(user);
}
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();
...
}
}
ArrayList throwing `ConcurrentModificationException` when trying to run `.size()` method
The most glaring issue you have there is that you're using ArrayList.subList()
while you don't seem to understand what it really does:
Returns a view of the portion of this list ... The returned list is backed by this list.
What you're storing in messagesInFlight
is a view, not a copy. When you delete the first messages from messages
, you're in fact deleting the very messages you had in your messagesInFlight
right after subList()
call. So after the for
loop, there will be completely different messages, and the first n messages will be completely lost.
As to why you are getting the error you see - subList()
specifically allows non-structural changes to both sub-list and the original list (non-structural means replacing the elements, not adding or removing them), and the example in the documentation also showcases how the original list may be modified by modifying the sub-list. However, modifying the original list and then accessing it through the sub-list is not allowed and may result in ConcurrentModifcationException
, similarly to what happens when you change a list you're iterating through with an iterator.
Related Topics
How to Create Recyclerview With Multiple View Types
Firebaselistadapter Not Pushing Individual Items For Chat App - Firebase-Ui 3.1
Running Code in Main Thread from Another Thread
How to Execute Bash Command With Sudo Privileges in Java
How Does the Java 'For Each' Loop Work
How to Efficiently Iterate Over Each Entry in a Java Map
Cannot Refer to a Non-Final Variable Inside an Inner Class Defined in a Different Method
Java: Maintaining Aspect Ratio of Jpanel Background Image
Run Java Class File from PHP Script on a Website
Why Does My Function That Calls an API or Launches a Coroutine Return an Empty or Null Value
Android 6.0 Multiple Permissions
How to Parse Xml Using the Sax Parser
How to Set Java_Home in Linux For All Users
How to Add Jtable in Jpanel With Null Layout