40

I was reading the answer mentioned to the question "Do we ever need to use Iterators on ArrayList?".

In the answer, the user stated something like this: "A big use case of iterators with ArrayLists is when you want to remove elements while iterating".

This could be achieved even using remove method of ArrayList in Java. My question is why we need iterator in ArrayList?

Consider the code:

import java.util.*;
public class ocajp66 {
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        for (int i = 0; i < 10; i++) {
            a.add(i);
        }
        System.out.printf("BEFORE ITERATOR\n");
        for (int i = 0; i < a.size(); i++) {
            System.out.printf("I:%d\n", a.get(i));
        }
        System.out.printf("AFTER ITERATOR\n");
        Iterator i = a.iterator();
        while (i.hasNext()) {
            System.out.printf("I:%d\n", i.next());
        }
    }
}

Can anybody explain the significance of the iterator? It would be wonderful if you could explain me with code.

Community
  • 1
  • 1
Karthik Rk
  • 707
  • 1
  • 10
  • 19

5 Answers5

60

As you have stated iterator is used when you want to remove stuff whilst you iterate over the array contents. If you don't use an iterator but simply have a for loop and inside it use the remove method you will get exceptions because the contents of the array changes while you iterate through. e.g: you might think array size is 10 at the start of the for loop but it wont be the case once you remove stuff.. so when u reach the last loops probably there will be IndexOutofBoundsException etc.

Dev Blanked
  • 8,555
  • 3
  • 26
  • 32
  • 9
    This is not the right answer. Iterators are a way to encapsulate arrays so you can't edit their contents. What you're talking about was added to iterators later on. – David Jan 16 '15 at 13:02
  • 6
    that's why you use array.size() for the for loop condition and not a magic number. – camel-man Feb 02 '16 at 15:19
16

It is clear that an ArrayList-like API could work without the iterator() method. However, an ArrayList is a Collection and the iterator() method is defined in the Collection interface ... so ArrayList has to implement it.

The point about deleting from an ArrayList is that doing it by indexing requires some thought:

    for (int i = 0; 
         i < a.size(); // Hoist this at your peril
         i++) {
        if (a.get(i) == something) {
            a.remove(i);
            i--;  // Leave this out at your peril
        }
    }

And it gets worse if you need to remove the list element in a method called from the loop ... 'cos the method has to then say that it has removed an element so that the caller can adjust the loop index.

A third reason why iterator is a good thing on an ArrayList is that it allows you to use Java 5's for (type var : iterable) ... syntax.

The bottom line is that you don't have to use iterators on ArrayList instances. If you don't want to, then don't.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • You can use that for loop notation on an ArrayList directly; the iterator isn't needed. Removing elements during traversal is the only reason. It'd be cool to have an ArrayList with a removal marker that somehow removed all the marked elements at the end of the loop. A man can dream :) – Rob Grant Oct 15 '13 at 11:32
  • Except modifying iteration variable is considered a bad practice and you should never ever do that. – pseudo Nov 29 '13 at 01:05
  • @pseudo - that is true, but I don't understand why you have mentioned it here. Is my answer or any of the comments *suggesting* that people should modify the loop variable?? – Stephen C Nov 29 '13 at 01:32
  • @RobertGrant: can't you just traverse the array backwards? Start at size-1, end at 0. – adam.r Jan 06 '14 at 00:24
  • 1
    @adam.r - I think you misunderstood Robert's comment. Traversing the array (list) backwards doesn't achieve anything, and certainly doesn't solve the problem of *efficiently and safely* removing elements. – Stephen C Jan 06 '14 at 01:28
  • @StephenC: the issue (as I understand it) is that the removal of the element changes the index of all the subsequent elements; therefore, there is no problem as long as you know that you will not be accessing any objects with indicies equal to or greater than your counter (i). You don't need any extra update for the counter, because the counter always be moving into the unmodified portion of the array (which backs the ArrayList). Or is there some other risk to removing objects while iterating over an ArrayList by index? – adam.r Jan 06 '14 at 02:06
  • @adam.r Oh, never thought of that. I'll have to think about it! And Stephen C, if there was a non-Iterator way of doing it, I wouldn't have to stay on my butt for as long, coding around Java :) – Rob Grant Jan 07 '14 at 14:47
9

This is an example of how it is possible to get the results you want in several different ways. This kind of redundancy is not unique to Java.

  • for (int i=0; i < myArray.length; i++) { ... }

This syntax was introduced in the very early versions of Java. It iterates over a usual Java array in a for { } loop. This is generally safe because Java arrays are fixed length and so "Index Out of Bounds" exceptions are not possible.

  • for (int i=0; i < myArrayList.size(); i++ { ... }

This syntax reflects a later release of Java, after the introduction of the Collections API which introduced ArrayList. Classes that implement the Collection interface, as already mentioned above, must implement an Iterator but you don't have to use it. This for { } loop doesn't, but the danger here is that ArrayLists are not fixed size. If it should shrink in the body of your for loop, and exception can result.

  • for (MyArrayType t : myArrayList) { }

This syntax was also released in a later release of Java. It is called the enhanced for loop. Any collection class that provides an Iterator by implementing the Iterable interface can take advantage of this syntax. This allows iterating over items in a collection without having to explicitly instantiate an Iterator. A favorite way to use this in a JavaFX Application is to loop through a bunch of controls to set a property to a value, eg. to reset the contents of a group of TextFields:

for (TextField tf : new TextField[] { txtf1, txtf2, txtf3, txtfa, txtfb, txtfc}) {
    tf.setText("");
}
  • while (myCollectionIterator.hasNext()) { }

You can always explicitly instantiate an Iterator. This is safe to use when collection size is changing (from the Collection's own methods). It correct to say that the Iterator is more closely a property of the Iterable interface than a feature of the core Java language. But you can still use it as a language-like feature (in the enhanced for loop) thanks to later Java releases.

These constructs provide redundancy, but they are not identical. There are nuances of each that enable one to be particularly useful at a given time. You should use all of them.

scottb
  • 9,908
  • 3
  • 40
  • 56
2

Q: Why do we need an iterator in ArrayList?

We don't - just as you've shown in your code, you can iterate and do core operations on an ArrayList without an iterator. But it's a nice to have feature.

Q: Can anybody explain the significance of the iterator?

In addition to its design value, one I could see is its fail-fast feature. I quote this paragraph from the ArrayList documentation:

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.

You were looking for code, you can actually see the ArrayList's iterator implementation here: ArrayList.java.

Jops
  • 22,535
  • 13
  • 46
  • 63
0

For your question, if we use list.remove() method instead of iterator.remove() then IndexOutOfBoundsException will be thrown.

list.remove() is safe to use if you put break statement once you find specific object/index to be removed so that it will be exited from the loop without any Exception(like IndexOutOfBoundsException )

Following iterator code still can throw ConcurrentModificationException if we use iterator EVEN in synchronized environment.

List<String> empNames = new ArrayList<String>();
        synchronized (empNames) {
            Iterator<String> iterator = empNames.iterator();
            while (iterator.hasNext()) {
                iterator.next();
                empNames.add("Another Name"); // throws
                // ConcurrentModificationException
            }
        }
AmitG
  • 10,365
  • 5
  • 31
  • 52