Java Language

Iterator and Iterable

Introduction#

The java.util.Iterator is the standard Java SE interface for object that implement the Iterator design pattern. The java.lang.Iterable interface is for objects that can provide an iterator.

Remarks#

It is possible to iterate over an array using the for-each loop, though java arrays do not implement Iterable; iterating is done by JVM using a non-accessible index in the background.

Using Iterable in for loop

Classes implementing Iterable<> interface can be used in for loops. This is actually only syntactic sugar for getting an iterator from the object and using it to get all elements sequentially; it makes code clearer, faster to write end less error-prone.

public class UsingIterable {

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1,2,3,4,5,6,7);
        
        // List extends Collection, Collection extends Iterable
        Iterable<Integer> iterable = intList;
        
        // foreach-like loop
        for (Integer i: iterable) {
            System.out.println(i);
        }
        
        // pre java 5 way of iterating loops
        for(Iterator<Integer> i = iterable.iterator(); i.hasNext(); ) {
            Integer item = i.next();
            System.out.println(item);
        }
    }
}

Using the raw iterator

While using the foreach loop (or “extended for loop”) is simple, it’s sometimes beneficial to use the iterator directly. For example, if you want to output a bunch of comma-separated values, but don’t want the last item to have a comma:

List<String> yourData = //...
Iterator<String> iterator = yourData.iterator();
while (iterator.hasNext()){
    // next() "moves" the iterator to the next entry and returns it's value.
    String entry = iterator.next();
    System.out.print(entry);
    if (iterator.hasNext()){
        // If the iterator has another element after the current one:
        System.out.print(",");
    }
}

This is much easier and clearer than having a isLastEntry variable or doing calculations with the loop index.

Creating your own Iterable.

To create your own Iterable as with any interface you just implement the abstract methods in the interface. For Iterable there is only one which is called iterator(). But its return type Iterator is itself an interface with three abstract methods. You can return an iterator associated with some collection or create your own custom implementation:

public static class Alphabet implements Iterable<Character> {

    @Override
    public Iterator<Character> iterator() {
        return new Iterator<Character>() {
            char letter = 'a';

            @Override
            public boolean hasNext() {
                return letter <= 'z';
            }

            @Override
            public Character next() {
                return letter++;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Doesn't make sense to remove a letter");
            }
        };
    }
}

To use:

public static void main(String[] args) {
    for(char c : new Alphabet()) {
        System.out.println("c = " + c);
    }
}

The new Iterator should come with a state pointing to the first item, each call to next updates its state to point to the next one. The hasNext() checks to see if the iterator is at the end. If the iterator were connected to a modifiable collection then the iterator’s optional remove() method might be implemented to remove the item currently pointed to from the underlying collection.

Removing elements using an iterator

The Iterator.remove() method is an optional method that removes the element returned by the previous call to Iterator.next(). For example, the following code populates a list of strings and then removes all of the empty strings.

List<String> names = new ArrayList<>();
names.add("name 1");
names.add("name 2");
names.add("");
names.add("name 3");
names.add("");
System.out.println("Old Size : " + names.size());
Iterator<String> it = names.iterator();
while (it.hasNext()) {
  String el = it.next();
  if (el.equals("")) {
    it.remove();
  }
}
System.out.println("New Size : " + names.size());

Output :

Old Size : 5
New Size : 3

Note that is the code above is the safe way to remove elements while iterating a typical collection. If instead, you attempt to do remove elements from a collection like this:

for (String el: names) {
    if (el.equals("")) {
        names.remove(el); // WRONG!
    }
}

a typical collection (such as ArrayList) which provides iterators with fail fast iterator semantics will throw a ConcurrentModificationException.

The remove() method can only called (once) following a next() call. If it is called before calling next() or if it is called twice following a next() call, then the remove() call will throw an IllegalStateException.

The remove operation is described as an optional operation; i.e. not all iterators will allow it. Examples where it is not supported include iterators for immutable collections, read-only views of collections, or fixed sized collections. If remove() is called when the iterator does not support removal, it will throw an UnsupportedOperationException.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow