Stream API in Java

In Java 8, every class which implements the java.util.Collection interface has a stream method which allows you to convert its instances intoStream objects.

  • Technically: typed interface
  • Extends: BaseStream
  • Also has: IntStream, LongStream, DoubleStream based on primitive data types int, long and double

Properties:

Does not hold data

  • pulls data from given source
  • connects to source, consumes the data and processes it

Does not modify data

  • because data is processed in parallel with no visibility issues
  • not enforced by compiler, merely a contract

Source may be unbounded

  • not finite meaning number of elements produced by the source is unknown at build time

Patterns for building

  • Stream.empty(); // an empty stream
    
  • Stream.of("one"); // singleton stream
    
  • Stream.of("one", "two", "three"); // stream with multiple elements
    
  • Stream.generate(() -> "one"); // constant stream using Supplier
    
  • Stream.iterate("+", s -> s + "+"); // a growing stream
    
    Made using UnaryOperator which takes an element and returns another of the same type. iterate takes the current element of the stream as the first argument, gets the result from the UnaryOperator of the second argument and uses that as the next element in the stream.
  • ThreadLocalRandom.current().ints(); // a random stream
    
  • IntStream stream = "hello".chars();
    
    chars method returns an int stream based on the letters of the characters.
  • IntStream stream = "hello".chars();
    
    chars method returns an int stream based on the letters of the characters. -
    Stream<String> words = Pattern.compile().splitAsStream(something); // stream on a regular expression
    
    The Pattern class implements the regular expression engine in JDK. It provides a Pattern object by calling the compile static method. -
    Stream<String> lines = Files.lines(path); // stream on the lines of a text file
    

Terminal v/s Intermediate operation

A terminal operation must be called to trigger processing on the stream. Without it, no data is consumed, processed or returned.

Example

forEach v/s peek. We cannot substitute forEach calls with peek calls as peek is an intermediate operation created for debugging purposes.

Differentiating

  1. Javadoc specifies terminal or intermediate
  2. A call that returns Stream -> intermediate. A call that returns something else or void -> terminal

Functions

  • map
  • filter
  • peek
  • forEach
  • skip
  • limit
  • Match Reductions returning boolean -> anyMatch (any element), allMatch (all elements), noneMatch ^^^ may not evaluate predicate for all elements -> short circuiting terminal operations
  • Find Reductions -> findFirst (returns any if the stream is unordered -> built on a set etc), findAny

Reduce Reductions

1. Provided the list has only positive numbers (since the identity for max is 0 over a positive range)

int maxNum = 
list.stream()
.reduce(0, (p1, p2) -> Integer.max(p1, p2));

2. If identity element is not provided, Optional is returned -> result of the reduction on an empty stream is not defined

Optional<Integer> maxNum = 
list.stream()
.reduce((p1, p2) -> Integer.max(p1, p2));