Tuesday, October 7, 2014

How to traverse Collection in Java using Iterator, for-each loop and forEach method

In this article, we look into difference method for iterating over the Collection such as Iterator, for-each loop and forEach method of Java 8 with examples. We also see the difference between them.

Most often, you need to access all elements of a collection one at a time. Different types of collections store their elements differently using different types of data structures. Some collections impose ordering on their elements and some do not. The Collections Framework provides the following ways to traverse a collection:

  • Using an Iterator
  • Using a for-each loop
  • Using the forEach() method
Some collections, such as lists, assign each element an index and they let you access their elements using indexes. You can traverse those collections using a regular for-loop statement as well. You can also traverse collections by converting them into streams and performing an aggregate operation on those streams.

A collection provides an iterator to iterate over all its elements. Sometimes an iterator is also known as a generator or a cursor. An iterator lets you perform the following three operations on a collection:

  • Check if there are elements that have not been yet accessed using this iterator.
  • Access the next element in the collection.
  • Remove the last accessed element of the collection.
The iterator itself does not impose any ordering in which it returns the elements from a collection. However, if the collection imposes ordering on its elements, the iterator will maintain the same ordering.

An iterator in Java is an instance of the Iterator<E> interface. You can get an iterator for a collection using the iterator() method the Collection interface.
The Iterator<E> interface contains the following methods:

  • boolean hasNext() : The hasNext() method returns true if there are more elements in the collection to iterate. Otherwise, it returns false.Typically, you call this method before asking the iterator for the next element from the collection.
  • E next() : returns the next element from the collection. You should always call the hasNext() method before calling the next() method. If you call the next() method and the iterator has no more elements to return, it throws a NoSuchElementException.
  • default void remove() : removes the element of the collection that was returned last time by calling the next() method of the iterator. The remove() method can be called only once per call to the next() method. If the remove() method is called more than once per next() method call or before the first call to the next() method, it throws an IllegalStateException
  • default void forEachRemaining(Consumer<? super E> action) : is new to Java 8 and takes an action on each element of the collection that has not been accessed by the iterator yet. The action is specified as a Consumer. For more information on this, please check my previous post on Lambda and Method reference.
The Collections Framework supports fast-fail concurrent iterators. You can obtain multiple iterators for a collection and all of them can be used to iterate over the same collection concurrently. If the collection is modified by any means, except using the remove() method of the same iterator after the iterator is obtained, the attempt to access the next element using the iterator will throw a ConcurrentModificationException. It means that you can have multiple iterators for a collection; however, all iterators must be accessing (reading) elements of the collection. If any of the iterators modify the collection using its remove() method, the iterator that modifies the collection will be fine and all other iterators will fail. If the collection is modified outside of all iterators, all iterators will fail.
The code uses method reference System.out::println as a Consumer for the forEachRemaining() method. Notice that using the forEachRemaining() method helps shorten the code by eliminating the need for a loop using the hasNext() and next() methods. Please refer method reference article .

Things to remember for Iterator

  • An Iterator is a one-time object.
  • You cannot reset an iterator. 
  • It cannot be reused to iterate over the element of the collection. 
  • If you need to iterate over the elements of the same collection again, you need to obtain a new Iterator calling the iterator() method of the collection.

for-each Loop
You can use the for-each loop to iterate over elements of a collection that hides the logic to set up an iterator for a collection.

You can use the for-each loop to iterate over any collection whose implementation class implements the Iterable interface. The Collection interface inherits from the Iterable interface, and therefore, you can use the for-each loop with all types of collections that implement the Collection interface. For example, the Map collection type does not inherit from the Collection interface, and therefore, you cannot use the for-each loop to iterate over entries in a Map.

The for-each loop is simple and compact. Behind the scenes, it gets the iterator for your collection and calls the hasNext() and next() methods for you. You can iterate over all elements of a list of string as follows:
The for-each loop is not a replacement for using an iterator. The for-each loop has several limitations, though. You cannot use the for-each loop everywhere you can use an iterator. For example, you cannot use the for-each loop to remove elements from the collection.
Another limitation of the for-each loop is that you must traverse from the first element to the last element of the collection. It provides no way to start from middle of the collection. The for-each loop provides no way to visit the previously visited elements, which is allowed by the iterator of some collection types such as lists.

forEach() Method
The Iterable interface contains a new forEach(Consumer<? super T> action) method that you can use in all collection types that inherit from the Collection interface. The method iterates over all elements and applies the action.
It works similar to the forEachRemaining(Consumer<? super E> action) method of the Iterator interface with a difference that the Iterable.forEach() method iterates over all elements whereas the Iterator.forEachRemaining() method iterates over the elements in the collections that have not yet been retrieved by the Iterator.

Tip  The Iterator is the fundamental way of iterating over elements of a collection. It has existed since the beginning. All other ways, such as the for-each loop, the forEach() method, and the forEachRemaining() method, are syntactic sugar for the Iterator. Internally, they all use an Iterator .

If you know anyone who has started learning java, why not help them out! Just share this post with them. Thanks for studying today!...

No comments:

Post a Comment