Java streams 14. Stream factory methods and operations

Technically, they are all methods that belong to the Stream interface. But some of them create a Stream object (we call them factory methods or just methods; they all are static), while others process the elements emitted by the created Stream object (we call them operations; they all are non-static).

All operations are divided into intermediate operations and terminal operations. Intermediate operations return a Stream object and, thus, allow other operations to be applied to the returned Stream object. Terminal operations either return values of other types or do not return anything at all (produce just side effects). They do not allow other operations to be applied and close the stream.

Stream factory methods

The following are factory methods of the Stream interface:

     
  static Stream<T> generate(Supplier<T> s);
  static Stream<T> iterate(T seed, UnaryOperator<T> f);
  static Stream<T> iterate(T seed, Predicate<T> hasNext, 
                                    UnaryOperator<T> next);
  static Stream.Builder<T> builder();
  static Stream<T> of(T... values);
  static Stream<T> empty();
  static Stream<T> ofNullable(T t);
  static Stream<T> concat(Stream<T> a, Stream<T> b);
    

The first five we have discussed already in this series:
static Stream<T> generate(Supplier<T> s);
static Stream<T> iterate(T seed, UnaryOperator<T> f);
static Stream<T> iterate(T seed, Predicate<T> hasNext, UnaryOperator<T> next);

static Stream.Builder<T> builder();
static Stream<T> of(T… values);
The other three – empty(), ofNullable(), and concat() – we will discuss in the upcoming article Stream factory methods.

Stream intermediate operations

The following are the intermediate operations that sort, skip and filter elements emitted by a Stream object:

   
  Stream<T> sorted();
  Stream<T> distinct(); 
  Stream<T> skip(long n);
  Stream<T> limit(long maxSize); 
  Stream<T> filter(Predicate<T> predicate); 
  Stream<T> sorted(Comparator<T> comparator);
  default Stream<T> dropWhile(Predicate<T> predicate);
  default Stream<T> takeWhile(Predicate<T> predicate);
    

The following are the intermediate operations that transform stream elements (map each of them to another value using the provided function):

    
  Stream<R> map(Function<T,R> mapper);
  IntStream mapToInt(ToIntFunction<T> mapper);
  LongStream mapToLong(ToLongFunction<T> mapper);
  DoubleStream mapToDouble(ToDoubleFunction<T> mapper);
    

And the rest is the intermediate operations that generate another stream from each original stream element using the provided function and embed this new stream into the original stream of elements:

     
  Stream<R> flatMap(Function<T, Stream<R>> mapper);
  IntStream flatMapToInt(Function<T,IntStream> mapper);
  LongStream flatMapToLong(Function<T,LongStream> mapper);
  DoubleStream flatMapToDouble(Function<T,DoubleStream> mapper); 
     

We will discuss all the above intermediate operations in the upcoming articles.

There is one intermediate operation that does not transform any of the elements:

  
  Stream<T> peek(Consumer<T> action); 
   

How do we know that this operation does not change stream elements? It uses Consumer function to do its work, and the Consumer function does not return anything (void). The following is an example of the peek() operation usage:

   
    List.of("1","2","3").stream()
        .map(s -> s + "a")
        .peek(System.out::println)
        .map(s -> s + "b")
        .forEach(System.out::println);
  

The output of the above code snippet is as follows:

  
  1a
  1ab
  2a
  2ab
  3a
  3ab
  

The peek() operation is typically used for debugging, to check the intermediate result.

Stream terminal operations

The following are all Stream terminal operations:

  
  long count();
  void forEach(Consumer<T> action);
  void forEachOrdered(Consumer<T> action);

  boolean allMatch(Predicate<T> predicate);
  boolean anyMatch(Predicate<T> predicate);
  boolean noneMatch(Predicate<T> predicate);

  Optional<T> findAny();
  Optional<T> findFirst();
  Optional<T> max(Comparator<T> comparator);
  Optional<T> min(Comparator<T> comparator);

  Object[] toArray();
  A[] toArray(IntFunction<A[]> generator);

  Optional<T> reduce(BinaryOperator<T> accumulator);
  T reduce(T identity, BinaryOperator<T> accumulator);
  U reduce(U identity, BiFunction<U,T,U> accumulator, 
                          BinaryOperator<U> combiner);

  R collect(Collector<T,A,R> collector);
  R collect(Supplier<R> supplier, BiConsumer<R,T> accumulator,
                                     BiConsumer<R,R> combiner);
   

The operation count() is straightforward. It returns the count of the elements emitted by the stream. Naturally, it makes sense to use with finite streams only. Otherwise, it never returns any value. Here is an example of its usage:

  
  long c = List.of("a","b","c").stream().count();
  System.out.print(c);            //prints: 3
   

The usage example of the forEach() operation you have seen in our previous example. It produces a side effect using the provided function and returns nothing. It can be used with an infinite stream.

The forEachOrdered() operation is useful when the stream processing happens in parallel (we will talk more about parallel processing in one of the subsequent posts). To demonstrate the difference between forEach() and forEachOrdered() operations, let us use them for parallel processing of the same stream:

   
    List.of("1","2","3","4")
        .parallelStream()
        .forEach(System.out::print);        //prints: 3421

    List.of("1","2","3","4")
        .parallelStream()
        .forEachOrdered(System.out::print); //prints: 1234
   

As you can see, the forEachOrdered() operation processes the stream elements in the order they were emitted by the stream, while the forEach() operation processes them in the order it receives the elements from the sub-streams processed in parallel, so the processing order may change with each run. 

We will discuss other terminal operations in the upcoming posts.

In the next post, we will talk about three factory methods – empty(), ofNullable(), and concat()

See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub

, ,

Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.

Powered by WordPress. Designed by Woo Themes