Chapter 3. Filtering Collections with Lambdas

3.1.  Develop code that iterates a collection by using the forEach() method and method chaining

[Note]

There are two general approaches to implementing an iterator depending on who controls the iteration:

  • Active (external) iterators

    For an active iterator (a.k.a. explicit iterator or external iterator), the client controls the iteration in the sense that the client creates the iterator, tells it when to advance to the next element, tests to see if every element has been visited, and so on.

    In Java 1.0 and Java 1.1, the two primary collection classes were Vector and Hashtable, and the Iterator design pattern was implemented in a class called Enumeration:

    Vector names = new Vector();
    names.add("George");
    names.add("Fred");
    names.add("Ron");
    
    Enumeration e = names.elements();
    while (e.hasMoreElements()) {
        String name = (String) e.nextElement();
        System.out.println(name);
    }
    								

    In Java 1.2 the Iterator design pattern was implemented in a class named Iterator. Casting an object returned from an Iterator was still necessary. For Java 1.2 through Java 1.4, iterating over a collection of strings looked like the code below:

    List names = new ArrayList();
    names.add("George");
    names.add("Fred");
    names.add("Ron");
    
    Iterator i = names.iterator();
    while (i.hasNext()) {
        String name = (String) i.next();
        System.out.println(name);
    }
    								

    Java 5 introduced generics, the interface Iterable, and the enhanced for-loop.

    
    // Java SE 5 !!!
    package java.lang;
    
    import java.util.Iterator;
    
    public interface Iterable<T> {
        public Iterator<T> iterator();
    }
    
    								

    
    package java.util;
    
    public interface Iterator<E> {
        public boolean hasNext();
        public E next();
        public void remove();
    }
    
    								

    NOTE: the for-loop is implemented internally with Iterator.hasNext() and Iterator.next() methods, so it can be considered as an active iterator.

    
    List<String> names = new ArrayList<String>();
    names.add("George");
    names.add("Fred");
    names.add("Ron");
    
    for (String name : names) {
        System.out.println(name);
    }
    
    								

  • Passive (internal) iterators

    For a passive iterator (a.k.a. an implicit iterator, internal iterator, or callback iterator), the iterator itself controls the iteration. The client essentially says to the iterator to perform some operation on the elements in the collection. With the release of Java 8, this approach is available to Java programmers.

    The java.lang.Iterable interface in Java 8 has new method - forEach:

    
    package java.lang;
    
    import java.util.Iterator;
    import java.util.function.Consumer;
    
    public interface Iterable<T extends Object> {
    
        public Iterator<T> iterator();
    
        public default void forEach(Consumer<? super T> cnsmr) {
            // ...
        }
    
        ...
    }
    
    								

    java.util.Collection interface extends java.lang.Iterable in Java 8, so all java.util.List or java.util.Set implementations now automatically have a forEach() method.

    java.util.Map interface does not extend java.util.Collection, but it also has since Java 8 the forEach() method for consistency.

    This method takes a single parameter that is a functional interface. Therefore the actual parameter passed to the forEach() method is a candidate for a lambda expression.

    
    List<String> names = new ArrayList<>();
    names.add("George");
    names.add("Fred");
    names.add("Ron");
    
    names.forEach(name -> System.out.println(name));
    
    								

    or using method reference syntax:

    ...
    names.forEach(System.out::println);
    ...
    								

    Note the difference between the passive iterator in code above and the active iterator in the previous section listings. In the active iterator listings, the loop structure controls the iteration, and during each pass through the loop, an object is retrieved from the list and then printed. In passive iterator listing, there is no explicit loop. We simply tell the forEach() method what to do with the objects in the list - in this case we simply print the object. Control of the iteration resides within the forEach() method.

Signature of forEach() method in java.util.List interface:


package java.util;

public interface List<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super E> cnsmr) {
        ...
    }

    ...
}

					

Signature of forEach() method in java.util.Set interface:


package java.util;

public interface Set<E extends Object> extends Collection<E> {

    /**
     * Performs the given action for each element of the Iterable until all elements have been
     * processed or the action throws an exception.
     */
    public default void forEach(Consumer<? super E> cnsmr) {
        ...
    }

    ...
}

					

Signature of forEach() method in java.util.Map interface:


package java.util;

public interface Map<K extends Object, V extends Object> {

    /**
     * Performs the given action for each entry in this map until all entries have
     * been processed or the action throws an exception.
     */
    public default void forEach(BiConsumer<? super K, ? super V> bc) {
        ...
    }

    ...
}

					

Professional hosting         Free 'Oracle Certified Expert Web Services Developer 6' Guide     Exam 1Z0-810: Upgrade to Java SE 8 Programmer Quiz