Java Iterators

Java Iterators Evolution: Deciphering Enumeration, Iterator, and List Iterator

Embark on a journey through the evolution of Java iterators! Explore Enumeration, Iterator, and List Iterator, deciphering their evolution and mastering their usage.

Understanding Cursor

A cursor is an object that allows us to access the elements of a collection one by one. It is useful when we want to traverse or manipulate the data stored in a collection. There are three types of cursors available in Java: enumeration, iterator, and list iterator. Each of them has its own advantages and limitations. In this article, we will learn about these cursors and how to use them in Java programs.



Enumeration

Enumeration is the oldest type of cursor in Java. It was introduced in JDK 1.0 and can be used to access the elements of legacy collections, such as Vector and Hashtable. We can create an enumeration object by calling the elements() method of the collection. For example:

Vector<Integer> v = new Vector<>();
v.add(10);
v.add(20);
v.add(30);

Enumeration e = v.elements(); // create an enumeration object
Java

Enumeration interface defines two methods: hasMoreElements() and nextElement().

  • The hasMoreElements() method returns true if there are more elements in the collection.
  • The nextElement() method returns the next element in the collection.

For example:

while (e.hasMoreElements()) {
  int i = (int) e.nextElement(); // get the next element
  System.out.println(i); // print the element
}
Java

The output of the above code is:

10
20
30

Limitations of Enumeration

Enumeration has some limitations that make it less preferable than other cursors. Some of them are:

  • Enumeration can only be used for legacy collections. It cannot be applied to any collection that implements the Collection interface, such as ArrayList, HashSet, LinkedList, etc.
  • Enumeration can only move forward. It cannot move backward or access the previous element.
  • Enumeration can only perform read operations. It cannot modify or remove the elements of the collection.


Java Iterator

Iterator is the most common type of cursor in Java. It was introduced in JDK 1.2 and can be used to access the elements of any collection that implements the Collection interface. It is called an “iterator” because “iterating” is the technical term for looping. To use an iterator, you must import it from the java.util package. We can create an iterator object by calling the iterator() method of the collection. For example:

ArrayList<Integer> a = new ArrayList<>();
a.add(10);
a.add(20);
a.add(30);

Iterator itr = a.iterator(); // create an iterator object
Java

Java Iterator interface defines three methods: hasNext()next() and remove().

  • The hasNext() method returns true if there are more elements in the collection.
  • The next() method returns the next element in the collection.
  • The remove() method removes the last element returned by the next() method.

For example:

while (itr.hasNext()) {
  int i = (int) itr.next(); // get the next element
  System.out.println(i); // print the element
  if (i == 20) {
    itr.remove(); // remove the element
  }
}
Java

The output of the above code is:

10
20
30

The final state of the collection is:

[10, 30]

Advantages of Iterator

Iterator has some advantages over enumeration that make it more versatile and powerful. Some of them are:

  • Iterator can be used for any collection that implements the Collection interface. It is a universal cursor that can traverse any type of collection.
  • Iterator can perform both read and remove operations. It can modify the collection by removing the elements while iterating.
  • Iterator is safer than enumeration. It throws a ConcurrentModificationException if the collection is modified by another thread or by another way while iterating.

Limitations of Iterator

Iterator also has some limitations that make it less suitable for some scenarios. Some of them are:

  • Iterator can only move forward. It cannot move backward or access the previous element.
  • Iterator can only remove the last element returned by the next() method. It cannot add or replace the elements of the collection.


List Iterator

List Iterator is a special type of iterator that can be used to access the elements of any collection that implements the List interface, such as ArrayList, LinkedList, Vector, etc. We can create a list iterator object by calling the listIterator() method of the list. For example:

LinkedList<Integer> l = new LinkedList<>();
l.add(10);
l.add(20);
l.add(30);

ListIterator litr = l.listIterator(); // create a list iterator object
Java

Java List Iterator interface extends the Iterator interface and defines some additional methods: hasPrevious()previous()nextIndex()previousIndex()add(), remove() and set().

  • The hasPrevious() method returns true if there are previous elements in the list.
  • The previous() method returns the previous element in the list.
  • The nextIndex() method returns the index of the next element in the list.
  • The previousIndex() method returns the index of the previous element in the list.
  • The add() method adds an element to the list.
  • The set() method replaces the last element returned by the next() or previous() method.
  • The remove() method removes the last element returned by the next() or previous() method.

For example:

while (litr.hasNext()) {
  int i = (int) litr.next(); // get the next element
  System.out.println(i); // print the element
  if (i == 20) {
    litr.set(25); // replace the element
  }
}
Java

The output of the above code is:

10
20
30

The final state of the collection is:

[10, 25, 30]

Advantages of List Iterator

List Iterator has some advantages over iterator that make it more flexible and convenient. Some of them are:

  • List Iterator can move in both directions. It can access the previous and next elements of the list.
  • List Iterator can perform read, remove, add, and replace operations. It can modify the list by adding, replacing, or removing the elements while iterating.

Limitations of List Iterator

List Iterator also has some limitations that make it less applicable for some situations. Some of them are:

  • List Iterator can only be used for lists. It cannot be applied to any collection that does not implement the List interface, such as HashSet, PriorityQueue, etc.


Fail-Fast and Fail-Safe Iterators

Iterators can be classified into two types based on how they handle concurrent modifications of the collection: fail-fast and fail-safe.

Fail Fast Iterators

Fail fast iterators immediately throw a ConcurrentModificationException if they detect that the collection has been modified during iteration. This means that they do not allow adding, removing, or changing any element from the collection while iterating over it. They do this by keeping track of an internal flag called modCount, which is updated each time the collection is modified. Fail fast iterators check the value of modCount before each iteration, and if it has changed since the iterator was created, they throw the exception.

The default iterators for collections from java.util package, such as ArrayListHashMap, etc., are fail fast. Here is an example of using a fail fast iterator on an ArrayList:

ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
  Integer number = iterator.next();
  System.out.println(number);
  numbers.add(50); // This will throw ConcurrentModificationException
}

Java

The advantage of fail fast iterators is that they prevent inconsistent or unexpected behavior due to concurrent modification. The disadvantage is that they require exclusive access to the collection, which may not be feasible in some scenarios.

Fail Safe Iterators

Fail safe iterators do not throw any exception if the collection is modified while iterating over it. This is because they operate on a clone or a snapshot of the collection, not on the original collection. Therefore, any changes made to the original collection are not reflected in the iterator. Fail safe iterators are also called weakly consistent, because they may not show the latest state of the collection.

The iterators for collections from java.util.concurrent package, such as CopyOnWriteArrayListConcurrentHashMap, etc., are fail safe. Here is an example of using a fail safe iterator on a CopyOnWriteArrayList:

CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.add(40);

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
  Integer number = iterator.next();
  System.out.println(number);
  numbers.add(50); // This will not affect the iterator
}
Java

The advantage of fail safe iterators is that they allow concurrent access and modification of the collection, which may be desirable in some scenarios. The disadvantage is that they consume more memory and may not show the most updated state of the collection.



Enumeration vs Iterator vs List Iterator

The following table summarizes the main differences between enumeration, iterator, and list iterator in Java.

FeatureEnumerationIteratorList Iterator
Applicable toLegacy collectionsAny collectionLists only
Legacy? Yes (1.0) No (1.2)No (1.2)
DirectionForward onlyForward onlyForward and backward
OperationsRead onlyRead and removeRead, remove, add, and replace
How can we get?By using elements() method of the Vector classBy using iterator() method of the Collection interfaceBy using listIterator() method of the List interface
Exception handlingNo exceptionConcurrentModificationExceptionConcurrentModificationException
MethodshasMoreElements()
nextElement()
hasNext()
next()
remove()
hasNext()
next()
nextIndex()
hasPrevious()
previous()
previousIndex()
remove()
add(Object o)
set(Object o)

How to Iterate Over a Map Using an Iterator?

To iterate over a map using an iterator, you need to first access a collection view of its keys, values, or entries using methods like keySet(), values(), or entrySet(). Then, simply obtain an iterator from this collection view by calling its iterator() method. For example:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class Main {

    public static void main(String[] args) {

        Map<String, Integer> map = new HashMap<>();
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);
        
        Iterator<Map.Entry<String, Integer>> itr = map.entrySet().iterator(); // create an iterator for the map entries

        while (itr.hasNext()) {
            Map.Entry<String, Integer> entry = itr.next(); // get the next entry
            System.out.println(entry.getKey() + " = " + entry.getValue()); // print the entry
        }

    }
}
Java

The output of the above code is:

A = 1
B = 2
C = 3


Things to Consider

Some important considerations to keep in mind when working with iterators in Java:

  • Concurrent Modifications: Be mindful of concurrent modifications to the underlying collection while iterating. Modifying the collection directly (outside of the iterator’s methods) can lead to ConcurrentModificationException in fail-fast iterators.
  • Fail-Fast vs. Fail-Safe: Understand the differences between fail-fast and fail-safe iterators and choose the appropriate type based on your application’s concurrency requirements. Fail-fast iterators immediately detect and throw exceptions on concurrent modifications, while fail-safe iterators allow modifications during iteration without throwing exceptions.
  • Traversal Direction: Decide whether you need bidirectional traversal (forward and backward) provided by ListIterators or if forward-only traversal suffices, as offered by standard Iterators. Choosing the appropriate traversal direction can optimize performance and simplify code.
  • Modification Operations: Utilize the remove() method of iterators to safely remove elements from the collection during traversal. Avoid modifying the collection directly while iterating, especially with fail-fast iterators, to prevent unexpected behavior and exceptions.
  • Collection Type Compatibility: Ensure that the iterator you choose is compatible with the type of collection you’re working with. For example, ListIterators are specifically designed for lists and cannot be used with other collection types like Sets or Maps.
  • Performance Considerations: Consider the performance implications of using iterators, especially when dealing with large collections. While iterators offer efficient traversal, excessive modifications or unnecessary cloning (in fail-safe iterators) can impact performance.
  • Thread Safety: Keep thread safety in mind when using iterators in a multi-threaded environment. If concurrent access to the collection is required, consider using thread-safe collection implementations or synchronizing access externally to prevent data corruption or race conditions.
  • Error Handling: Implement appropriate error handling mechanisms when using iterators, especially when dealing with potential exceptions like NoSuchElementException or UnsupportedOperationException. Handling these exceptions gracefully can improve the robustness of your code.
  • Immutable Collections: Keep in mind that iterators do not support modifications to immutable collections. If you need to modify a collection, ensure that it is mutable or create a mutable copy of the immutable collection before iterating.


FAQs

When should we use an enumeration instead of an iterator?

How do I choose between fail-fast and fail-safe iterators?

Can I use Iterators with all types of collections in Java?

What happens if I try to use an Iterator on a collection that doesn’t support modification, like an unmodifiable collection?

Can I use Iterators or ListIterators to traverse arrays in Java?

How do I know if an Iterator or ListIterator has reached the end of the collection?

Are there any alternatives to Iterators or ListIterators for traversing collections in Java?

Conclusion

In conclusion, when it comes to traversing collections in Java, Enumeration, Iterator, and List Iterator each offer unique functionalities. Enumeration is best for older collections, Iterator is versatile for most collections, and List Iterator adds extra features for lists. Understanding these options allows developers to make informed choices, optimizing their Java code for efficiency and clarity.

Learn More

#

Interested in learning more?

Check out our blog on Java UUID: Ensuring Uniqueness in Your Applications

Top Picks for Learning Java

Explore the recommended Java books tailored for learners at different levels, from beginners to advanced programmers.

Disclaimer: The products featured or recommended on this site are affiliated. If you purchase these products through the provided links, I may earn a commission at no additional cost to you.

1
Java: The Complete Reference
13th Edition

Java: The Complete Reference

  • All Levels Covered: Designed for novice, intermediate, and professional programmers alike
  • Accessible Source Code: Source code for all examples and projects are available for download
  • Clear Writing Style: Written in the clear, uncompromising style Herb Schildt is famous for
2
Head First Java: A Brain-Friendly Guide

Head First Java: A Brain-Friendly Guide

  • Engaging Learning: It uses a fun approach to teach Java and object-oriented programming.
  • Comprehensive Content: Covers Java's basics and advanced topics like lambdas and GUIs.
  • Interactive Learning: The book's visuals and engaging style make learning Java more enjoyable.
3
Modern Java in Action: Lambdas, streams, functional and reactive programming
2nd Edition

Modern Java in Action: Lambdas, streams, functional and reactive programming

  • Latest Java Features: Explores modern Java functionalities from version 8 and beyond, like streams, modules, and concurrency.
  • Real-world Applications: Demonstrates how to use these new features practically, enhancing understanding and coding skills.
  • Developer-Friendly: Tailored for Java developers already familiar with core Java, making it accessible for advancing their expertise.
4
Java For Dummies
8th Edition

Java For Dummies

  • Java Essentials: Learn fundamental Java programming through easy tutorials and practical tips in the latest edition of the For Dummies series.
  • Programming Basics: Gain control over program flow, master classes, objects, and methods, and explore functional programming features.
  • Updated Coverage: Covers Java 17, the latest long-term support release, including the new 'switch' statement syntax, making it perfect for beginners or those wanting to brush up their skills.

Add a Comment

Your email address will not be published.