Stream API

A Stream represents a sequence of elements and provides different kinds of operations to perform upon those elements.

Processing

Stream Operations

Stream operations are either intermediate (returns a Stream object and can be chained) or terminal (returns void or non-stream result).

A Stream pipeline is executed only when there is a terminal operation (like count(), collect(), forEach(), etc.). Otherwise, no operation on the stream will be performed. Once the terminal operation is performed, the Stream is finished and can’t be reused.

Any Stream can potentially have an unlimited number of elements going through it. In cases where it’s possible, stream elements are processed individually as they arrive:

persons.stream()
  .filter(person -> {
    Boolean isMatched = person.age > 30;
    System.out.println(person + ", FILTER MATCH: " + isMatched);
    return isMatched;
  })
  .map(person -> {
    System.out.println("Extract age: " + person.age);
    return person.age;
  })
  .map(personAge -> {
    String ageStr = personAge + " y.o.";
    System.out.println("Convert to a string: " + ageStr);
    return ageStr;
  })
  .toList();

/********** OUTPUT: **********
// Person: { name: Terry Medhurst, age: 50, weight: 75.4 }, FILTER MATCH: true
// Extract age: 50
// Convert to a string: 50 y.o.
// Person: { name: Sheldon Quigley, age: 28, weight: 74.0 }, FILTER MATCH: false
// Person: { name: Terrill Hills, age: 38, weight: 105.3 }, FILTER MATCH: true
// Extract age: 38
// ...

Processing Order

A Stream object’s processing can be sequential or parallel.

  • Sequential Mode. The elements are processed in the same order they arrive. For the ordered stream sources (SortedMap, List, etc.), the processing order is guaranteed.

  • Parallel Mode. The order of elements processing is not guaranteed because multiple threads on multiple cores can be used.

Initialization

  1. The Collection interface provides two methods to generate a Stream: stream() and parallelStream():
List<String> namesList = List.of("Terry", "Sheldon", "Terrill", "Miles", "Mavis");
Stream<String> namesStream = namesList.stream();
  1. Create a new Steam object from the list of items using Stream.of():
Stream<String> namesStream = Stream.of("Terry", "Sheldon", "Terrill", "Miles", "Mavis");
  1. Convert an array to a stream using Arrays.stream():
String[] names = { "Terry", "Sheldon", "Terrill", "Miles", "Mavis" };
Stream<String> namesStream = Arrays.stream(names);
  1. Create Stream using built-in IntStream/DoubleStream/LongStream classes functionality:
IntStream numbers = IntStream.range(0, 10);       // 10 is exclusive
IntStream numbers = IntStream.rangeClosed(0, 10); // 10 is inclusive

Closing

General Streams doesn’t have to be closed. It’s required to close streams that work with IO-channels:

try (Stream<String> lines = Files.lines(Paths.get("somePath"))) {
  lines.forEach(System.out::println);
}

The Stream interface declares the onClose() method which will be called when the stream is closed:

public Stream<String>streamAndDelete(Path path) throws IOException {
  return Files.lines(path).onClose(() -> someClass.deletePath(path));
}

Examples

Resources

Top