1

I have some questions about the different behaviors of Iterator on the main Java Framework Collection classes (not only for the List class).

  1. List

If I write for-each I will have an exception:

Collection<String> cc= new ArrayList<>(3);
        cc.add("Cio");
        cc.add("Mio");
        cc.add("Tio");
for (String s:cc) {
    System.out.println(s);
    cc.remove(s);            //Exception
}

If I use the Iterator, I will have not an exception:

for (Iterator<String> it =cc.iterator(); it.hasNext();) {
    String s =it.next();
    if (s.startsWith("C"))
        it.remove();
}
  1. ArrayDeque

This is different for ArrayDeque, infact if I use for-each, I will have not an exception:

ArrayDeque<String> greetings = new ArrayDeque<String>();
        greetings.push("hello");
        greetings.push("hi");
        greetings.push("ola");
        greetings.pop(); 
        greetings.peek();
        while (greetings.peek() != null)
        System.out.print(greetings.pop());

But if I use the iterator, I will have an exception:

Iterator<String> it = greetings.iterator();
        while(it.hasNext()) {
            System.out.println(greetings.pop()); //Exception
        }

Why? And does the iterator throw an exception for the other JFC collections, in particular: HashSet, TreeSet, LinkedList?

Thanks a lot!

A.

Sam
  • 536
  • 5
  • 23
  • 1
    Iterator does not reflect the changes in the underlying list. – ghoul932 Apr 06 '19 at 13:33
  • Possible duplicate of [Is List.iterator() thread-safe?](https://stackoverflow.com/questions/5847939/is-list-iterator-thread-safe) – ghoul932 Apr 06 '19 at 13:33
  • @ghoul932 but only for the list? Why in the ArrayDeque does it (iterator) throw an exception? In your link the argument is only List. I'd like to know the behavior of the other JFC collections. – Sam Apr 06 '19 at 14:11
  • 2
    You have not used `it.next()`. There are some Collections which provide consistency. Check out the package `java.util.concurrent`. – ghoul932 Apr 06 '19 at 14:40
  • [Two types of iterators.](https://stackoverflow.com/questions/17377407/what-are-fail-safe-fail-fast-iterators-in-java) – ghoul932 Apr 06 '19 at 14:50

1 Answers1

3

ArrayList

The list maintains a modCount field that is incremented each time a structural modification is done to the list.

Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

Further...

If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a ConcurrentModificationException in response to the next, remove, previous, set or add operations. This provides fail-fast behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.Use of this field by subclasses is optional.

If a subclass wishes to provide fail-fast iterators (and list iterators), then it merely has to increment this field in its add(int, E) and remove(int) methods (and any other methods that it overrides that result in a structural modification the list.

The two peice of list iteration code :

1.

for (String s:str1) {
    System.out.println(s);
    str1.remove(s);  
}

and

2.

Iterator<String> i1 = str.iterator();
while(i1.hasNext()) {
    i1.next();
    i1.remove();
}

--may seem identically but are internally a bit different.

Its worth mentioning that the iterator of the list maitains a expectedModCount. That should be in sync with modCount when modifying the list while iterating.

In 1st case, String s:str1 gets the iterator, checks hasNext() and calls the next(), just like in the 2nd case. The difference comes in remove() method call. str1.remove(s); calls the remove method of the ArrayList. This increments the modCount but not expectedModCount. So in the second iteration, when next() is called it throws ConcurrentModificationException. On the other hand, in 2nd case, i1.remove(); calls the remove method from Iterator implementation in ArrayList. This increments the the modCount and expectedModCount and -- Bingo.

Note: Missing the i1.next(); in the second scenario will cause IllegalStateExcepton. This is because the cursor for the next element in the list is not updated.

TakeAway: Dont call the list.remove(element) method while iterating the list. This method is meant to be called when not in an iteration.

ArrayDeque

If you iterate the ArrayDeque like this:

Iterator<String> i1 = str.iterator();
while(i1.hasNext()) {
    i1.next();
    i1.remove();
}

-- it works the exact same way as its ArrayList counterpart.

When calling pop() or push() method of the ArrayDeque class, you don't actually iterate on the queue, you just modify the head or tail of the queue. This is just like calling remove() method of the ArrayList class when not in Iteration (not the remove() of Iterator of ArrayList). This doesn't qualify to be a structural modification. So it doesn't throw an Exception.

Refer this article.

raviiii1
  • 936
  • 8
  • 24