Why arrayList is not iterating at last element? [duplicate] - java

This question already has answers here:
Collection throws or doesn't throw ConcurrentModificationException based on the contents of the Collection [duplicate]
(5 answers)
Closed 6 years ago.
I have tried the following two scenarios:
ArrayList arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println("---->" + value);
if (value.equals("1")) {
arrayList.remove(0);
}
}
The first scenario output is :
---->1
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at Test.main(Test.java:83)
Probably the ConcurrentModificationException exception is caused because I'm trying to modify the collection while I'm still iterating it.
In the second scenario I changed the if() condition from value.equals("1") to value.equals("2") and the program does not throw any error.
ArrayList arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println("---->" + value);
if (value.equals("2")) {
arrayList.remove(0);
}
}
The second scenario output is:
---->1
---->2
In both the scenarios, my program does not reach the 3rd element (Value : 3).
Can you please help me to understand the problem ?

Problem is you are removing element from the list which presend at index 0. Instead of removing from the list use iterator.remove() method. For more info read this.
program code -
List arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println("---->" + value);
if (value.equals("2")) {
iterator.remove();
}
}
Why list not returning the last element?
Answer -
you are removing element from the list and you already perform iterator action against same list. At the end iterator.hasnext() method returns false so you are not getting last element.
If you want to see it for demo purpose run below sample codes -
List arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println("---->" + value);
if (value.equals("2")) {
arrayList.remove(0);
}
}
System.out.println("ArrayList : "+arrayList);
output -
---->1
---->2
ArrayList : [2, 3]
List arrayList = new ArrayList();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = (String) iterator.next();
System.out.println("---->" + value);
if (value.equals("2")) {
iterator.remove();
}
}
System.out.println("ArrayList : "+arrayList);
output -
---->1
---->2
---->3
ArrayList : [1, 3]

By running two pieces of code in debug mode, I found the following.
1st scenario:
Elements saved from arraylist:
After the remove command has been executed:
The ConcurrentModificationException gets thrown at the following line
String value = (String) iterator.next();
2nd Scenario:
The ArrayList will be the same as the second link.
After printing 2, the control breaks from
while (iterator.hasNext())
as iterator.hasNext() = null. Hence it's safe.

I suppose that you encounter the problem only in the first case because you are trying to remove the element pointed by the iterator. In fact, the second scenario succeeds because you remove the first arraylist element that, at that moment, is not pointed by the iterator.
(p.s. I agree with all the various and future answers that will describe you how to solve your problem by showing fragments of code, I just provided you an explanation of what happens)

The ConcurrentModificationException occurs because you are removing the same element (at index 0) that the current iteration of the loop is reading (the one with value = ”1”). In the second case you don't see the exception because you are removing the element at index 0, but are reading the element at index 1 (the one with value = "2"). You cannot remove an element concurrently while reading it unless you use one of the collections that supports concurrency (see java.util.concurrent package)
You are removing an element (remove(0)) while iterating the list, which "shifts the end of the list" towards left by one. So, the next time you check for iterator.hasNext(), it will return false and the original 3rd element never gets processed.

When you iterate on a Collection, you should remove the element with the iterator :
iterator.remove()
In the second code, here :
if (value.equals("2")) {
arrayList.remove(0);
}
you remove the first element of the list. The list has so a size of 2 elements.
The iterator implementation takes into consideration the size of the list to know if it has next elements or not :
public class ArrayList<E> extends AbstractList<E>
...
private class Itr implements Iterator<E> {
public boolean hasNext() {
return cursor != size;
}
}
Since two elements were iterated and that the list has now a size of 2 because you remove one element of it, the iterator considers that it has not next element to iterate on.
If you use an iterator to remove the current element, you have not this side-effect.

Related

Why ConcurentModificationException isn't always thrown when we remove element in a for loop [duplicate]

This question already has an answer here:
ConcurrentModificationException not thrown consistently
(1 answer)
Closed 1 year ago.
G'day everyone. Today I get a quite weird situations regarding ConcurentModificationException.
I have a list like this.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
I also have three pieces of code like this.
This will print [2, 3].
for (int i = 0; i < list.size(); i++) {
Integer integer = list.get(i);
if (integer.equals(1)) {
list.remove(integer);
}
}
System.out.println(list);
This will throw exception.
for (Integer integer : list) {
if (integer.equals(1)) {
list.remove(integer);
}
}
System.out.println(list);
This will print [1, 3].
for (Integer integer : list) {
if (integer.equals(2)) {
list.remove(integer);
}
}
System.out.println(list);
I used to think that removing an element inside a for-loop always results ConcurrentModificationException but I might be wrong. Can you guys tell me what create the difference here.
I run the code on Corretto 11.
The point here is that the ConcurrentModificationException is thrown when you access(!) the next element via the iterator, not when checking for the existance of a further element.
In the second snippet you remove the first element then there are still two to go. For the next iteration it will be noticed, that there are more elements which will be accessed and as the expectedModCount changed, the Exception will be thrown.
In the third example however, due to the reduced size, you will have reached the end after the second iteration and no further access will take place and therefore no exception is thrown.
The first example is not meant to throw an exception, because you are not using an iterator, which would check for the concurrent modification in the first place.
As was mentioned earlier first code snippet is not supposed to throw ConcurrentModificationException, because you are not iterating over list.
As regards to other two snippets the equivalent code is following:
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
Iterator it = list.iterator();
while(it.hasNext()) {
Integer integer = (Integer)it.next();
if (integer.equals(2)) {
list.remove(integer);
}
}
System.out.println(list);
and if you take a look at java.util.ArrayList.Itr implementation:
public boolean hasNext() {
return cursor != size;
}
#SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
you can find out that the reason of such behaviour is caused by cursor = i + 1 in next() call: removing penultimate element in ArrayList decreases size and followed hasNext() call returns false.
A regular for loop and an enhanced one are, slightly, different.
While the regular one loops over the list and gets the individual elements one by one, the enhanced one uses an Iterator. The enhanced for loop works with Iterable objects or an array.
Basically the enhanced for loop translates to this
for(Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
Integer integer = it.next();
if (integer.equals(2)) {
list.remove(integer);
}
}
This also explains why the first enhanced loop throws an exception. As according to the iterator there should be more elements. Calling the next method detects that the underlying collection has changed and thus throws a ConcurrentModificationException.
The last one doesn't because you are already at the end of the iterator and thus no additional call to next.
When you remove an element at index 0, the list shrinks, therefore your list with [1,2,3] is now [2,3]. you then remove index 1 wich remove the 3. You have now [2] in your list. Then you remove index 2 which no longer exist.
You may want to remove only index 0 at every iteration or remove from the last index to the first.

Iterating a list until the list is empty

I'm trying to iterate a list, wherein each iteration I'm doing one of the below:
Continue to next element (by some logic)
Removing the current element
What I need is that after I'm iterating through all the items, it will continue to iterate until the list is empty (the logic ensures all elements will be removed).
Problem is that after the iterator iterates all the list elements, it didn't continue to run on the elements I didn't remove:
List<Integer> lst = new ArrayList();
lst.add(1);
lst.add(2);
lst.add(3);
Iteartor<Integer> iterator = lst.listIterator();
while (iterator.hasNext()){
Integer curInt = iterator.next();
if (!passTest(curInt)){
continue;
}
iterator.remove();
}
IMPORTANT NOTE: passTest(curInt) logic can be different for each iteration. It means that iteration one can cause continue, then the second and third iterations will cause a removal. PROBLEM is I'm expecting for a fourth iteration (on the first item that wasn't removed).
The solution I've found:
List<Integer> lst = new ArrayList();
lst.add(1);
lst.add(2);
lst.add(3);
Iteartor<Integer> iterator = lst.listIterator();
while (!lst.isEmpty()){
Integer curInt;
if (iteration.hasNext()){
curInt = iterator.next();
} else {
curInt = lst.get(0);
}
if (!passTest(curInt)){
continue;
}
iterator.remove();
}
Is that the right way to achieve that?
Your solution doesn't seem correct. You will first iterate over all the elements of the List, possibly removing some of them. Once you finish iterating over the List, iteration.hasNext() will always return false, so you'll keep getting the first element (due to curInt = lst.get(0)). If you remove that first element, you'll get a different element the next time curInt = lst.get(0) is executed, so the List will become empty in the end, but that doesn't seem like the desired behavior (if it was the desired behavior, you could eliminate the Iterator and just keep getting the first element in a loop and possibly removing it).
You should use nested loops, and re-create the Iterator instance inside the outer loop:
while (!lst.isEmpty()) {
Iteartor<Integer> iterator = lst.listIterator();
while (iterator.hasNext()) {
Integer curInt = iterator.next();
if (passTest(curInt)) {
iterator.remove();
}
}
}
Each iteration of the outer loop creates an Iterator and performs the inner loop.
Each iteration of the inner loop iterates over the elements of the List, and possibly removes some of them.
Once you finish an iteration over the List, you must create a new Iterator in order to iterate over the remaining elements again.

Java for each loop working

I was working on certain task, when incidentally did something wrong according to me but the code executed and provided correct result. I was little surprised and had question in mind how all these for each loop works.
Example (sample program),
public static void main( String[] args )
{
String myInput = "hello , hi , how are you ";
String[] splitted = myInput.split(",");
List<String> mylist = new ArrayList<String>();
for (String output : splitted)
{
mylist.add(output);
}
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
for (String output : splitted)
{
mylist.add(output);
}
for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
}
I was curious to know and while searching I found one more post that said we can remove elements from list if we used iterator approach, So I tried,
for (String output : splitted)
{
mylist.add(output);
}
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
Now I just want to know what is happening behind the every for each loop quoted above.
I want to know the technical aspect, I know I can not modify the collection in for each loop but in some case stated above it worked why?
Now I just want to know what is happening behind the every for each
loop quoted above
1. for (String output : splitted)
{
mylist.add(output);
}
This adds each output String from splitted array to the mylist list.
2. for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
The for statement is governed by the following production:
for ( FormalParameter : Expression )
Statement
where Expression must be an instance of java.lang.Iterable, or an array. So this for:each loop is equivalent to this:
Iterator<String> iterator = mylist.iterator();
while (iterator.hasNext()) {
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
Here mylist.iterator() will return a new instance of Iterator type:
public Iterator<E> iterator() {
return new Itr();
}
So even if you are creating new ArrayList instances and assigning them to mylist on each iteration, the iterator obtained from the original mylist will still have a reference to the original mylist and will keep iterating through the elements of original mylist. The iterator keeps a reference to the list it was created on. The assignment mylist = new ArrayList<String>() has no effect on the data that the iterator works on because it changes the variable mylist and not the list itself.
3. for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
Below statement explains this behavior. It is copied from Arraylist doc:
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.
4. for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
The above statement also explains the behavior of this for loop: the list can be structurally modified by the iterator's own remove or add methods while iterating through the list.
A for-each loop is possible for Classes that implement Iterable. This also means that you can create Classes yourself which you can use in for-each loops, which can be very comfortable.
This interface forces you to implement a method iterator() which returns an Iterator. Then the for-each loop does nothing but retrieve that iterator and iterate over it using hasNext() and next(). Just the same as you would do it yourself.
The problem with removing is that when you use a for-each loop and then remove an element from the List, the constructed Iterator will not know anything about that change and there will be a ConcurrentModificationException.
But if you call Iterator.remove() directly, the Iterator will know about that change and can handle it.
A common little trick to avoid Iterators and Exceptions at the same time is to do something like this:
List<Object> objects = new ArrayList<Object>();
for (Object object : new ArrayList<Object>(objects)) {
objects.remove(object);
}
So you create a temporary copy of that List, iterate over that, but call remove on the original List.
for-each loop of List will be internally converted to for loop with iterator.
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
gets converted to
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
String output = (String)iterator.next();
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
And since the the snapshot of list is already taken at below
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
The loop is running until the last element of list i.e. "how are you".
Whereas, below is not working because of FailFast behaviour of List.
for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
It says, if you are modifying the list while iterating, with anything other than iterator's own remove method, List will throw ConcurrentModificationException and thats the reason the below is working.
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
That is corrent. You cannot modify value of collection that is beeing iterated over using "foreach" loop, and to do that, you have to use collection's iterator.
It is of course not a problem to add something to a completely different list than the one you are currently traversing, as you did with the line mylist = new ArrayList<String>(); Even though the variable still has the same name, it will point to an entirely different list.
The reason why you cannot add something to a list that is currently being "walked through" is, that the internal implementation of that list might not be able to ensure, that you still get the same order of elements and especially not all remaining elements as you would expect.
This can be understand best if you imagine that you are using a sorted list: you put in a new element, but whether or not you see that element is undefined, as it depends on where you are and what you insert. As Java doesn't know if you are ok with that, it takes the safe road and throws an Exception.
There are however lists that are well capable of being able to be modified during traversal, mostly the concurrent lists in the concurrent package.

ArrayList Method: iterator()

I cannot find out what this method does. Can someone please explain?
Example:
for (Iterator ite = list.iterator(); ite.hasNext(); )
Thanks, I just started learning java. Currently grade 9.
It's just "reading" the list with an iterator (useful if you want to remove elements from the list while reading).
You can get an element from the iterator with
Item item = ite.next();
if you want to remove the item from the list you can do simply
ite.remove();
You can loop a list also with simpler for, like
for (Item item : list) {
System.out.println(item.toString());
}
or
for (int i = 0; i<list.size(); i++) {
Item item = list.get(i);
System.out.println(item.toString());
}
but with these, if you try to do list.remove(item) or list.remove(i) you will raise an exception (ConcurrentModificationException if I'm not wrong).
At a very basic level, an iterator "walks through" the list until it gets to the end. So in that method, you're creating an object that points at one object in the list. When you call next() on the iterator, it moves to the next object in the list. And when you call hasNext() on the iterator, you're asking whether the iterator has anywhere else to go, or whether it's at the end of the list. hasNext() will return true if the iterator has a next() and false otherwise.
You likely want to create your for loop like this:
for ( Iterator iter = list.iterator(); iter.hasNext(); iter.next() )
which creates a new iterator for the list (the first element). Each time the loop comes back, it calls iter.next() which makes the iterator point to the next element in the list. When you've gotten to the end of the list, iter.hasNext() is no longer true, and so the loop breaks.
In short, you're just creating a for loop that goes through every element of the list.

retainAll changes the last element

List<Left> resultList = lists.iterator().next();
for (Iterator iterator = lists.iterator(); iterator.hasNext();) {
List<Left> list = (List<Left>) iterator.next();
resultList.retainAll(list);
}
/*for (Left left : resultList) {
System.out.println(left.getData());
}*/
for (Left left : mLeft) {
ArrayList<Left> mTempList = left.getArray();
for (Iterator iterator = mTempList.iterator(); iterator.hasNext();) {
Left left2 = (Left) iterator.next();
System.out.println(left2.getData());
}
}
I am trying to find out the common elements , my original list is getting changed.I mean only last element gets changed when I print the lists.
Any suggestions.
resultList gets assigned to the first element in your collection lists right at the top.
On line 4 you execute retainAll against resultList without changing what resultList is assigned to - it's still pointing to the first object in lists.
You might consider creating a new List<Left> object to check for the common elements, right at the start instead of how you've declared resultList:
List<Left> resultList = new List<Left>();
resultList.addAll ((List<Left>) lists.iterator().next());
...
You would then want to use resultList in the final loop when you print out the elements.
Note that you are still assuming that lists has at least one element - if it's empty you'll get an exception.

Categories

Resources