Reputation: 15033
I'm trying to add an Integer to an array list using and iterator. It crashes at the third call to .next() of the iterator. Here is the stack trace and code
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:886)
at java.util.ArrayList$Itr.next(ArrayList.java:836)
at quicksort.test.generateSorted(test.java:33)
at quicksort.test.main(test.java:17)
Java Result: 1
public static int[] generateSorted(final int length, final int minVal, final int maxVal)
{
ArrayList<Integer> data = new ArrayList<>(length);
data.add(getRandomVal(minVal, maxVal));
ListIterator<Integer> itr = data.listIterator();
boolean added;
for(int i = 0; i < length; i++)
{
added = false;
int rndNum = getRandomVal(minVal, maxVal);
while(itr.hasNext() && !added)
{
System.out.println(rndNum);
Integer currentNum = itr.next();
if(currentNum >= rndNum)
{
itr.add(rndNum);
added = true;
}
}
if(!added)//add to end of arrayList
data.add(rndNum);
}
return data.stream().mapToInt(i -> i).toArray();
}
How can it be a concurrent modification when everything in the project is single threaded? If an itterator can't add
As an aside, what's the difference between new ArrayList<>(size) and new ArrayList(size)? Netbeans gives me a warning with the second but still compiles fine.
EDIT: oops typo, I also meant to ask what the difference is with ArrayList(size)
Upvotes: 0
Views: 602
Reputation: 2088
The exception is due to you modifying the List while still using an Iterator. Iterators need to maintain state between calls to keep track of where it is. If you modify the underlying List, the Iterator's state becomes invalid, and it can no longer guarantee correct operation. This means you can not use any methods that modify a List, while you are currently using an Iterator. You can still modify the List through an Iterator because the Iterator can now maintain it's internal state and guarantee correct operation on later calls to the iterator.
There are a couple of solutions to your problem. The first is to recreate the iterator every time you you edit the underlying list. You should be doing this anyways as it appears you are trying to keep the list sorted and if you don't start at the beginning you won't have a good sort. The second solution is to add all the numbers into the list and then sort it using something like Collections.sort
Solution 1:
List< Integer > list = new ArrayList<>();
for( int i = 0; i < length; i++ ) {
Iterator< Integer > itr = list.listIterator();
// Generate and add number to list
}
Solution 2:
List< Integer > list = new ArrayList<>();
for( int i = 0; i < length; i++ ) {
int num = ...; // Generate number
list.add( num );
}
Collections.sort( list );
The warning is because you are not providing generic arguments to a generic class. It goes away when you add the '<>' because that tells the compiler to infer the generic arguments. It was recently added to reduce the amount of code required to create something like this.
List< Map< String, Map< String, List< String > > > > list1 = new ArrayList< Map< String, Map< String, List< String > > > >();
// Becomes
List< Map< String, Map< String, List< String > > > > list2 = new ArrayList<>();
Upvotes: 3
Reputation: 73528
Your data.add(rndNum);
is adding directly to your ArrayList
without using the ListIterator
. That causes the iterator to throw ConcurrentModificationException
when it notices that the list has changed.
Upvotes: 1
Reputation: 115328
ConcurrentModificationException
is thrown when collection that is being iterated using iterator is modified during the iteration not via the iterator.
This is exactly what you do in line:
data.add(rndNum);
when you access the list directly. Use itr.add()
instead as you did in previous fragment of your code sample.
Upvotes: 1