Using the forEachRemaining() Method – Collections: Part II
Using the forEachRemaining() Method
A convenient way to perform a task on each element of a collection is to use the forEachRemaining() method of the Iterator<E> interface. This method requires a consumer as an argument (§13.7, p. 709). The lambda expression defining the consumer is executed for each remaining element of the collection. In Example 15.1, a new iterator is obtained at (5) as the previous one was exhausted, before calling the method at (6). The lambda expression passed as an argument prints the name in uppercase (postfixed with a space for readability in the output). Note that the method is invoked on the iterator, and not directly on the collection.
iterator = collectionOfNames.iterator(); // (5)
iterator.forEachRemaining(name -> // (6)
System.out.print(name.toUpperCase() + ” “));
In Example 15.1, the toString() method of the collection class is used implicitly in print statements to generate a text representation for the collection. The collection classes override the Object.toString() method to provide a text representation of their contents. The standard text representation generated by the toString() method for a collection is
[element1, element2, …, elementn]
where each elementi, where 1 <= i <= n, is the text representation generated by the toString() method of the individual elements in the collection.
Using the for(:) Loop to Iterate Over a Collection
A for(:) loop can be used to iterate over an array or a data structure that implements the java.lang.Iterable<E> interface. This interface requires the implementation of the iterator() method to obtain an iterator that is used behind the scenes to iterate over elements of the data structure in a for(:) loop.
In the for(:) loop construct
for (type variable : expression) statement
the value of expression can be a reference value that refers to a collection that implements the Iterable<E> interface. In Figure 15.2 we see that the Collection<E> interface extends the Iterable<E> interface, and therefore, all collections that implement the Collection<E> interface can be iterated over using the for(:) loop. A collection that implements the Collection<E> interface and thereby the Iterable<E> interface has the element type E. This element type E must be assignable to the type of the variable in the for(:) loop. The variable is assigned the reference value of a new element in the collection each time the body of the loop is executed.
The semantics of the for(:) loop also apply when iterating over a collection. In particular, any structural change to the collection (adding or removing elements) in the for(:) loop will result in a ConcurrentModificationException—in other words, the underlying collection is structurally immutable.
Example 15.1 illustrates using a for(:) loop to iterate over a collection. The collection of names is iterated over in the for(:) loop at (7), printing each name in uppercase.
for (String name : collectionOfNames) { // (7)
System.out.print(name.toUpperCase() + ” “);
}
Behind the scenes, however, an appropriate iterator is used to iterate over the collection, but the for(:) loop simplifies iterating over a collection in the source code.
Note that if the collection is ordered or sorted, the iterator will iterate over the collection in the ordering used to maintain the elements in the collection. For example, in the case of an ArrayList, the iterator will yield the elements in the same order as the insertion order. In the case of a TreeSet<E>, the iterator will yield the elements in the sort order used to maintain the elements in the set. If the collection is unordered, the order in which the iterator will yield the elements is not predictable. Thus we cannot be sure in which order the elements of a HashSet will be iterated by the for(:) loop.