One of the most widely used and most common API in Java is iterator for the collections. If we have the list of objects in an array or list objects, the iterator is very useful for iterating the objects without much extra coding. Here we discuss about the comparison of two key interfaces, Iterable and Iterator with simple examples.
Also Read:
Questions:
- Why should we implement Iterable and Iterator interfaces in Java?
- How should we implement Iterable/Iterator interfaces?
Lets look at a simple example.
public class BasicStack<item> { public final int CAPACITY = 1000; private Item s[]; private int N; @SuppressWarnings("unchecked") public BasicStack() { s = (Item[]) new Object[CAPACITY]; } public Item pop() { Item item = s[--N]; s[N] = null; return item; } public void push(Item item) { s[N++] = item; } }
Code above implements a simple Stack data structure. Suppose we would like to expose this data structure to the client. To iterate over this data structure, clients have to use Iterator.
public static void main(String[] args) { BasicStack<integer> arrays = new BasicStack<integer>(); arrays.push(4); arrays.push(8); arrays.push(31); arrays.push(7); arrays.pop(); arrays.pop(); Iterator<integer> i = arrays.iterator(); while(i.hasNext()) { System.out.println(i.next()); } }
So far good. The client code above isn’t complicated to iterate over the data structure except that clients have to code their own iterators. Since we all know about Java enhanced for-loops, wouldn’t it be nicer if the clients were able to use enhanced for-loops on our data structures instead of using their own iterators.
Iterator<integer> i = arrays.iterator(); while(i.hasNext()) { System.out.println(i.next()); }
and
for(Integer i: arrays) System.out.println(i);
Same code has been rewritten using enhanced for-loops which looks more elegant. But enhanced for-loops can’t be used automatically in data structures that we implement. Then what do we do? This is where Iterable interface comes in. Only, if our data structure implemented Iterable interface, client can iterate using enhanced for-loops. The advantage of implementing Iterable interface is that the client needn’t create any manual iterators to iterate over our new data structures and can use the elegant enhanced for-loops. So, we should implement Java Iterable interface in order to make our code more elegant.
Iterable Interface
public interface Iterable<item> { Iterator<item> iterator(); }
Iterable interface is pretty simple. We need to implement an java.util.Iterator interface which can iterate over any collection.
Iterator Interface
public interface Iterator<item> { boolean hasNext(); Item next(); void remove(); }
Iterator interface has three methods: hashNext() which checks if there is a next item, next() which retrieves the next item and then remove() which removes from the collection, the last element retrieved. Generally, java experts suggest not to override remove() method and rather return some exception, when this method is invoked. Having looked at the basics, lets look at an implementation of Iterable and Iterator interfaces with an example.
public class BasicStack<item> implements Iterable<item> { public static final int CAPACITY = 1000; private Item s[]; private int N; @SuppressWarnings("unchecked") public BasicStack() { s = (Item[]) new Object[CAPACITY]; } public Item pop() { Item item = s[--N]; s[N] = null; return item; } public void push(Item item) { s[N++] = item; } public int size(){ return N; } @Override public Iterator iterator() { return new ArrayIterator(); } private class ArrayIterator implements Iterator<item> { private int i = N; @Override public boolean hasNext() { return i > 0; } @Override public Item next() { return s[--i]; } @Override public void remove() { throw new UnsupportedOperationException(); } } }
The class BasicStack implements Iterable interface and hence iterator() method should be overridden. iterator() method invokes a new instance of ArrayIterator private class which implements java.util.Iterator class and overrides all its methods. This ArrayIterator class has all the logic for implementing iterators.
Looking at the code, hasNext() method checks if i>0 i.e., just checks if there is a valid next item. next() method returns the current item in the array and then decrements the counter to point to the next element. As suggested before, remove() method throws an Exception in case user tries to call this remove() method. It is up to the implementors choice to decide whether to implement remove() method or not to implement it. So when clients uses enhanced for-loops, Java internally calls next() first and then next() to iterate over the entire collection.
As it can be seen, it is pretty simple to implement Iterable and Iterator interfaces. Once this has been implemented, the clients can iterate our new data structures using the enhanced for-loops instead of having to write their own iterators.
That’s it about iterable and iterators. Hope you enjoyed reading.
This article is originally published at Java tutorials – Lets jump into the ocean, re-posted here with authors permission and as part of the JBC program.