Enumeration and Iterator in Java

Introduction

Java has two ways to iterate over the elements of a collection – using an Enumeration and an Iterator. This post explains what Enumeration and Iterators are. By the end of the post, you will understand the differences between them and have an understanding of when to use them. Last, we will look at interoperability between them.

Iterating through Collections

I already wrote a post on the Iterator Design Pattern. It provides a way to access the elements of a collection sequentially without exposing its underlying representation. In that, I showed how to implement an iterator for a custom aggregate object.

An Enumeration and an Iterator are generic interfaces in Java providing the same functionality. A collection will have a method to create an enumeration or an iterator. Once created, we can use it to iterate (step through) the individual elements.

Once we finish iterating the collection, attempting to get the next element results in an exception. To walk over from the beginning again we have to obtain a fresh instance of enumeration/iterator.
Note: There are implementations of an Iterator (like ListIterator) that extends the functionality of an Iterator adding the ability to walk backwards too (and other features). These are out of scope of this post.

At a high level, they support two methods 

  • A method returning a boolean – To check if there are more elements in the collection
  • Method to get the next element

We will explore these for an Enumeration and an Iterator and learn how it is implemented in Java.

Enumeration

We can create an Enumeration from legacy collection objects like Vector or a HashTable by calling the elements() method on it.

Methods

boolean hasMoreElements()
Returns true if the enumeration has more elements to be traversed and false otherwise.

E nextElement()
Returns the next element if the enumeration has at least one more element left. It throws NoSuchElementException if no more elements are left. This happens when we call nextElement when hasMoreElements returns false. (E is the type of the element returned),

Example

The below code creates an Enumeration from a Vector and walks through each element, printing it.

Vector<String> vector = new Vector<>();
vector.add("a");
vector.add("b");
vector.add("c");
Enumeration<String> enumeration = vector.elements();
while (enumeration.hasMoreElements()) {
    System.out.println(enumeration.nextElement());
}

Outputs:

apple
orange
pear

Iterator

We can create an Iterator from any Collection by calling the iterator method. 

Methods

boolean hasNext()
Returns true if the iteration has still elements left and false otherwise. (Similar to hasMoreElements of an Enumeration).

E next()
It returns the next element in the iteration (Similar to nextElement of an Enumeration).

Example

The below code creates an ArrayList and obtains an Iterator using the iterator method. It iterates through the iterator and prints each element (output is the same as when using a vector/enumeration).

List<String> list = new ArrayList<>();
list.add("apple");
list.add("orange");
list.add("pear");

Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}

Similarities between an Enumeration and an Iterator

1) Both are generic interfaces.
2) As seen so far, both are used to iterate through a collection. 
3) The mechanism used to iterate is also similar. They both have a method to check if there are elements left and a method to return the next element.

Differences between an Enumeration and an Iterator

Let us now look into the differences between an Enumeration and an Iterator.

Difference #1 – Enumerations is legacy

The Enumeration was added way back in Java 1.0 whereas Iterator was added later in 1.2. Iterating using an Enumeration is only available on old collection classes like Vector and HashTable. Modern Java applications do not use these to a large extent. We generally use other Collections like ArrayList, LinkedList, HashMap etc,. i.e., those added from Java 1.2 onwards. These do not support Enumerations. 
The Collection interface do not support Enumerations and only supports obtaining an Iterator.

Difference #2 – Iterators support removing

In addition to the two methods seen so far, Iterator has a remove method. Calling it when iterating removes the current element i.e., it removes the element that would be returned by the next() method. Thus, Iterators allow modification of the collection too whereas an Enumeration is read-only.

Difference #3 – Fail-safe vs Fail-fast

What will happen if the original collection is structurally modified when an iteration is in progress?

Enumeration – Structural modification

An Enumeration is fail safe. It does not throw any exception when the underlying collection is modified when an iteration is in progress. With the current implementation, it just proceeds with the iteration even though the collection is modified. But, we cannot guarantee the exact behaviour and it depends. The javadoc states this explicitly.

If the vector is structurally modified while enumerating over the elements then the results of enumerating are undefined.

Let us look at a couple of examples.

Example #1:
Let us add a new element to the vector after we process the second element

Enumeration<String> enumeration = vector.elements();
while (enumeration.hasMoreElements()) {
    String element = enumeration.nextElement();
    System.out.println(element);
    //add to vector
    if (element.equals("orange")) {
        vector.add("fig");
    }
}

This outputs

apple
orange
peach
fig

This shows us that the internal iterating cursor just proceeds, continuing to print the rest of the elements, and it also prints the newly added element.

Example #2:
Let us add the new element as the first one in the list

Enumeration<String> enumeration = vector.elements();
while (enumeration.hasMoreElements()) {
    String element = enumeration.nextElement();
    System.out.println(element);
    //add to vector at head
    if (element.equals("orange")) {
        vector.add(0, "fig");
    }
}

After printing apple, it prints orange infinitely – a never ending loop. This is because, whenever we encounter orange we insert a new element to the beginning. This shifts the rest of the elements by one to the right. When the cursor moves to the right, it would encounter orange again. This will again insert fig as the first element, shifting the rest of the elements to the right by one. This goes on and on and on resulting in an never ending printing of oranges.

Iterator – Structural modification

Let us see how an Iterator handles this.

Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
    //add to list
    list.add("fig");
}

This throws a ConcurrentModificationException. Nice and clear. The only way to structurally modify a collection when we are iterating it is to use the methods provided by the Iterator itself. To remove a element, use the remove method of the iterator. 

A plain iterator does not allow us to insert elements. But this is supported by the ListIterator APIs. Thus an Iterator is fail-fast.

Applying the Adapter pattern

We can easily make an Enumeration look like an Iterator and vice-versa. We can do this by applying the Adapter Design Pattern.

Enumeration as an Iterator

The Enumeration interface has a default method, asIterator, that adapts it as an Iterator. Thus, when you have an Enumerator, you can easily make it (adapt) as an Iterator and can pass it along where an Iterator is expected.
The implementation of it is quite simple (implementation code as of JDK 8).

default Iterator<E> asIterator() {
    return new Iterator<>() {
        @Override public boolean hasNext() {
            return hasMoreElements();
        }
        @Override public E next() {
            return nextElement();
        }
    };
}

The hasNext and next methods of the returned Iterator instance call the hasMoreElements and nextElements methods of the Enumeration respectively. We can use it as,

//Obtaining an Iterator from an enumeration
Iterator<String> iterator = vector.elements().asIterator();
    while(iterator.hasNext()) {
        System.out.println(iterator.next());
}

Iterator as an Enumeration

If we have to achieve the result other way around (getting an Enumeration from an Iterator) the JDK does not support that. But it is trivial to achieve this and can by done by writing our own adapter.

public class IteratorAdapter<E> implements Enumeration<E> {
    private final Iterator<E> iterator;

    public IteratorAdapter(Iterator<E> iterator) {
        this.iterator = iterator;
    }

    @Override
    public boolean hasMoreElements() {
        return iterator.hasNext();
    }

    @Override
    public E nextElement() {
        return iterator.next();
    }
}
Enumeration<String> enumeration = new IteratorAdapter<>(list.iterator());
    while(enumeration.hasMoreElements()) {
        System.out.println(enumeration.nextElement());
}

We pass the Iterator to the adapter as the adaptee. The adapter delegates to the right method of the Iterator when we call the target interface’s methods.

Note: If these terms sounds alien, check out the post on the Adapter Design Pattern.

Enumeration and Iterator – Which one to use?

Now you might ask – “Which one to use in your application?”

The answer is – Iterators

Enumeration is an old interface supporting only legacy collections. Hence, for all the new code you write, it is always recommended to use an Iterator to iterate over a collection. If you are dealing with legacy code working with Enumeration, you already know how to adapt an Iterator to an Enumeration and vice-versa. 

The Enumeration’s javadoc also has this recommendation.

The functionality of this interface is duplicated by the {@link Iterator} interface. In addition, {@code Iterator} adds an optional remove operation, and has shorter method names. New implementations should consider using {@code Iterator} in preference to {@code Enumeration}. It is possible to adapt an {@code Enumeration} to an {@code Iterator} by using the {@link #asIterator} method.

Conclusion

In this post, we looked at Enumeration and Iterator interfaces in Java. We looked at examples using them. Next, we saw the similarities or the common features, followed by the major differences between them. We also applied the adapter design pattern to adapt between an Enumeration and an Iterator.

Leave a Reply