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.
Related
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.
I have a very basic question.
I have created simple ArrayList and I am removing the item while iterating using for-each loop. It gives me java.util.ConcurrentModificationException because I can't remove an item while iterating but when I un-comment the if condition it works fine.
Please can anybody explain me how for-each works in this way.
ArrayList<String> list1 = new ArrayList<String>();
list1.add("Hello");
list1.add("World");
list1.add("Good Evening");
for (String s : list1) {
//if (s.equals("World")) {
list1.remove(1);
//}
}
If I change it to list1.remove(2); or list1.remove(0); then also its working fine.
Note: This is sample code and I know it will work fine using Iterator. My sole purpose of this question is to know how method remove() works perfectly if condition is un-commented no matter what index you are removing from the list.
The list has a variable called modCount, which means "modification count". Whenever you call remove (or perform other structural modifications), it increments the modCount.
The iterator can't keep track of its position in the list if you are adding or removing elements without telling the iterator. So as a safety check, at the start of iteration, the iterator makes a note of the modCount, saving it as expectedModCount. When each item is read from the iterator, the iterator checks to make sure the modCount still equals the expected value, and throws an exception if it doesn't.
Usually, this will successfully cause the exception to be thrown if the list is unsafely modified during iteration. However, it's not sufficient in this case when the if statement is enabled. After your code has read "World", that item is removed, and so the list now contains ["Hello", Good Evening"]. The iterator is still at position 1 (which now contains "Good Evening") and when it tries to read the next item, it finds it has now reached the end of the list, so it doesn't bother to check the modCount. Hence, no exception.
Note the caveat in the ConcurrentModificationException documentation: "It is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis."
Even if it doesn't happen to throw the exception in this case, the code is still wrong. To remove an element while iterating, you must use the iterator's own remove method:
for (Iterator<String> it = list1.iterator(); it.hasNext();) {
String s = it.next();
if (s.equals("World")) {
it.remove();
}
}
That way, the iterator knows that the list has changed and can still iterate correctly.
Alternatively, you can iterate from a temporary copy of the list:
for (String s : new ArrayList<>(list1)) {
if (s.equals("World")) {
list1.remove(...);
}
}
Although, in this simple case, you don't even need to do that; you can just write:
list1.remove("World");
You can also use an index-based removal. The drawback of this solution is that the list1.size() gets evaluated during every loop iteration. The positive thing is that removing an item from a List by its index is faster.
for (int i = 0; i < list1.size(); /* i incremented in loop body */) {
if ("World".equals(list1.get(i))) {
list1.remove(i);
}
else {
i++;
}
}
Use an Iterator and call remove():
Iterator<String> iter = list1.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
i know that we shouldn't modify the ArrayList during iteration.
But i'm using Iterator to iterate over list and iterator.remove() to remove the element but still results in ConcurrentModification Exception.
My program is not multithreaded.
I've many arraylist [class contains it and i'm processing many array of objects]
for(int i=0;i<obj.length;i++)
{
if(k==i) continue;
it = obj[i].arraylist.iterator();
while(it.hasNext()){
value = it.next();
if(condn) {
it.remove();
obj[k].arraylist.add(value);
//k and i are not same
}
}
}
"Note that Iterator.remove is the only safe way to modify a collection
during iteration; the behavior is unspecified if the underlying
collection is modified in any other way while the iteration is in
progress."
You can remove objects but not add new ones during the iteration, that's why you get that ConcurrentModificationException.
http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html
Edit:
You can also check:
if(k==i || obj[i].arraylist == obj[k].arraylist) continue;
You can only modify the List during iteration using it variable.
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.
I'm iterating a java.util.LinkedList and in some cases I add an element to it.
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
Schedule schedule = iterator.next();
if(condition)
iterator.add(new Schedule());
}
The problem is that if I start for example with one item, the new item is added before the next iterator.next() call and the iteration exits.
How can I append the item at the end of the LinkedList while iterating?
Please don't tell me to use another list and iterate it after the first because it doesn't solve my problem correctly.
Assuming you don't have a hard requirement to use an iterator then you can just shelve it and 'iterate' over the list by index instead:
LinkedList<Schedule> list;
for (int i = 0; i < list.size(); i++) {
final Schedule schedule = list.get(i);
if(condition)
list.add(new Schedule());
}
If you cannot use another list, you could solve your problem by keeping a count of the number of elements you processed via the iterator and compare that to the original size of the list: all new element will be at the end of the list, so you can end your loop when you have reached the original size.
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
int origSize = queue.size();
int currCount = 0;
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
++currCount;
if (currCount >= origSize) {
break; // reached the end of the original collection
}
Schedule schedule = iterator.next();
if(condition)
iterator.add(new Schedule());
}
You could also use an extra list to keep track of the new elements and add that to the original list after the processing is over:
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
LinkedList<Schedule> addQueue = new LinkedList<Schedule>();
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
Schedule schedule = iterator.next();
if(condition)
addQueue.add(new Schedule());
}
queue.addAll(addQueue);
Also, note that iterator.add()
Inserts the specified element into the list (optional operation). The element is inserted immediately before the next element that would be returned by next, if any, and after the next element that would be returned by previous, if any. (If the list contains no elements, the new element becomes the sole element on the list.) The new element is inserted before the implicit cursor: a subsequent call to next would be unaffected, and a subsequent call to previous would return the new element. (This call increases by one the value that would be returned by a call to nextIndex or previousIndex.)
so if you have more than one elements in the list, it will not add the new ones to the end, but between the current one and the one returned by next(). If you indeed want to place the new elements at the end of the list, use queue.add(...)
In general, it is not advisable to modify a collection while traversing it via an iterator, so I suggest you use the second approach (collect the extra elements in a separate list and add them to the original at the end)
As others have suggested, there is no efficient, off-the-shelf support for end-insertion during iteration in the current Collections framework. One suggestion involved rewriting the iterator. But I say, why not go a couple steps further?
Use reflection to modify the accessibility of the Node class, which gives you references to the double links, so you don't need to start iteration over with get(index), which should not be used in performant code.
Inherit/derive from LinkedList and override judiciously.
This is a no-brainer, but in the same spirit as 2 above, since the JDK is open-source, borrow the source as needed and write your own implementation.
In either case, I really feel this feature should be provided by Java LinkedList API.
How can I append the item at the end of the LinkedList while
iterating?
public void addWork(Scheduler scheduler)
{
synchronized(scheduler)
{
queue.addLast(scheduler);
}
}
and you can use queue.removeFirst() to deal with item in the queue from top-to-down.
public synchronized Scheduler getWork()
{
return queue.removeFirst();
}
Edited.
The requirements to add while iterating and include the added items in the iteration can only be met if you don't use an iterator for iteration since there is no way to recompute the state of your iterator every time an element is added. If you accept the less efficient get method to do your iteration, the problem is trivial. For example
LinkedList<Schedule> queue = new LinkedList<Schedule>(){{add(new Schedule());add(new Schedule());add(new Schedule());}};
int i = 0;
// queue.size() is evaluated every iteration
while (i < queue.size()) {
Schedule schedule = queue.get(i);
if(i++ % 2 == 0)
queue.add(new Schedule());
}
System.out.println(queue.size());
prints 6 as expected.