Beware forEach terminals

When using streams, the Stream.forEach method can be a problem for beginners. Many introductory examples use forEach to print the result of a computation. This is understandable, but don't be misled into thinking that forEach is commonly used as a terminal operation. It's not. The problem is that forEach exists in order to create side-effects, and side-effects are not in the spirit of purely-functional programming.

In practice, instead of using Stream.forEach, you'll usually want to return a Collection, or compute a final answer. This is called a reduction operation.

Example

import java.util.LinkedHashSet;
import java.util.Set;
import static java.util.stream.Collectors.*;
import java.util.stream.Stream;

/** @since Java 8 */
public class ForEachSideEffects {

  /**
   Stream.forEach can only act upon each item in the stream.
   It must necessarily do an action of some sort with each item in 
   the Stream. That is, forEach can only be used by invoking side-effects.
  */
  void example(){
    Stream.of(Atom.values())
      .filter(Atom::isAlwaysUnstable)
      //side-effect: print the item to the console:
      .forEach(System.out::println)
    ;
  }

  /**
   Inappropriate! This is not in the spirit of functional programming!
   This can lead to subtle problems.  
  */
  void badExample(){
    //defined completely outside the Stream code 
    Set<Atom> unstables = new LinkedHashSet<>(); //external state
    
    Stream.of(Atom.values())
      .filter(Atom::isAlwaysUnstable)
      //side-effect: changing external state (the Set)!:
      .forEach(atom -> unstables.add(atom));
    ;
  }

  /**
   Instead of using forEach, with its attendant side-effects, consider 
   'reducing' the Stream to some result. Here, the Stream is 
   reduced to a Set. 
  */
  void avoidForEach(){
    //use the stream code to 'reduce' the data to a Set, with no side effects 
    Set<Atom> unstables =  
      Stream.of(Atom.values())
      .filter(Atom::isAlwaysUnstable)
      //no side-effect: 
      .collect(toSet()) //static import of Collectors 
    ;
    
    //use the Set here
    for(Atom unstable : unstables){
      //...do something
    }
  }
  
  /**
   Some data from the periodic table of the elements (incomplete!). 
  */
  enum Atom {
    H("Hydrogen", 1, false),
    Tc("Technetium", 43, true),
    Au("Gold", 79, false),
    Pb("Lead", 82, false),
    Pu("Plutonium", 94, true),
    Fm("Fermium", 100, true);
    private Atom(String name, Integer numProtons, Boolean isAlwaysUnstable){
      this.name = name;
      this.numProtons = numProtons;
      this.isAlwaysUnstable = isAlwaysUnstable;
    }
    Boolean isAlwaysUnstable() { return isAlwaysUnstable; }
    String getName() { return name;  }
    Integer getNumProtons() { return numProtons; }
    private String name;
    private Integer numProtons;
    private Boolean isAlwaysUnstable;
  }
}
 

See Also :
Understand the functional style