placeholderJava Stream

Java Stream

An introduction to the Stream API introduced in Java 8

Overview

Java 8 introduced the Stream API for operating on collections. It abstracts a collection of elements as a stream: elements flow through a pipeline like water, and we can conveniently apply various operations to the stream to get the result we want. This style is called stream processing.

Operation categories:

  • Intermediate operations: return a new Stream and can be chained
  • Terminal operations: return a result or side-effect, and must come last

Below is an example of operating on a Collection with a Stream, where filter is an intermediate operation and forEach is a terminal operation.

public static void main(String[] args) {    List<String> nameList = Arrays.asList("Valerie" "Jack" "John" "Jerry" "Jesus");    nameList.stream()            .filter(name -> name.startsWith("J"))            .filter(name -> name.length() == 4)            .forEach(System.out::println);}[OUTPUT]JackJohn

Stream characteristics:

  • Lazy Evaluation: intermediate operations are not executed immediately. The stream only starts a real traversal when it hits a terminal operation, so multiple operations (mapping, filtering, etc.) run in a single pass. This property is called lazy evaluation, and it lets the Stream API work more efficiently
  • Internal Iteration: traditional collection operations use external iteration, where the user iterates over every element manually. The Stream API uses internal iteration: you only tell the Stream API what operations to perform on the collection, without caring about the actual iteration process
  • Immutability: a Stream does not modify the underlying data structure; it only operates on the original data and returns a new Stream
  • Parallel Processing: the JDK source comments note that Stream pipelines may execute either sequentially or in parallel, meaning a Stream can be processed sequentially or in parallel, taking full advantage of multi-core processors
Stream

Methods

MethodPurposeOperation type
countCount elementsterminal
forEachIterate and processterminal
reduceReduceterminal
collectCollectterminal
findAnyFindterminal
anyMatchMatchterminal
filterFilterintermediate
sortedSortintermediate
distinctDeduplicateintermediate
limitTake the first fewintermediate
skipSkip the first fewintermediate
mapMapintermediate
concatConcatenateintermediate

To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

Creating a Stream

// 使用 Collection  stream()和 parallelStream()方法List<Integer> list = Arrays.asList(1 2 3);Stream<Integer> stream1 = list.stream();Stream<Integer> stream2 = list.parallelStream();// 使用 Arrays  stream() 方法Stream<Integer> stream3 = Arrays.stream(new Integer[] {1 2 3});// 使用 Stream 类的静态方法 of()、iterate()、generate()Stream<Integer> stream4 = Stream.of(1 2 3); // of() 内部调用的是 Arrays.stream() methodStream<Double> stream5 = Stream.iterate(10.0,num -> num > 0.5 num -> num / 2);stream5.forEach(System.out::println);// 10.0 5.0 2.5 1.25 0.625 0.3125Stream<Double> stream6 = Stream.generate(Math::random).limit(5);stream6.forEach(System.out::println);// 5 个随机小数

Method Walkthrough

filter

Stream<T> filter(Predicate<? super T> predicate)
Returns a stream consisting of the elements of this stream that match the given predicate.

The filter method takes a Predicate functional interface instance functional_interface used to filter the elements of the stream, and returns a new stream containing the elements that match the condition.

Predicate is a functional interface added in Java 8. It takes one argument and returns a boolean; we usually create Predicate instances with lambda expressions.

// Commonly used methods in Predicatestream.filter(element -> element.length() > 5)stream.filter(element -> element.startsWith("J"))stream.filter(element -> element.endsWith("y"))stream.filter(element -> element.equals("Jerry"))stream.filter(element -> element.contains("e"))stream.filter(element -> element.matches(".*[aeiou].*"))

forEach, find, match

Streams support traversal and lookup operations similar to Collection. Note that elements in a Stream come wrapped in Optional.
Traverse

  • void forEach(Consumer<? super T> action)
    forEach takes a Consumer functional interface used to traverse every element in the stream and act on it.
    Consumer is a functional interface added in Java 8. It takes one argument and returns nothing, for example System.out::println;

Find

  • Optional<T> findFirst()
    Returns an Optional describing the first element of this stream, or an empty Optional if the stream is empty. If the stream has no encounter order, then any element may be returned.
  • Optional<T> findAny()
    Returns an Optional describing the first element of this stream, or an empty Optional if the stream is empty. If the stream has no encounter order, then any element may be returned.

Match

  • boolean anyMatch(Predicate<? super T> predicate)
    Returns true as long as at least one element matches the given condition
  • boolean allMatch(Predicate<? super T> predicate)
    Returns true only if all elements match the given condition
  • boolean noneMatch(Predicate<? super T> predicate)
    Returns true only if no element matches the given condition
//forEachlist.stream().forEach(System.out::println);// FindOptional<String> first = list.stream().findFirst();Optional<String> any = list.stream().findAny();// Matchboolean isExist = list.stream().anyMatch(element -> element.length() > 5);boolean isAllMatch = list.stream().allMatch(element -> element.startsWith("J"));

map, flatMap

map, as the name suggests, maps the elements of one Stream to another Stream. The code below maps a Stream of String to a Stream of Integer.

  • <R> Stream<R> map(Function<? super T,? extends R> mapper)
    Returns a stream consisting of the results of applying the given function to the elements of this stream.

  • <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
    Returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Each mapped stream is closed after its contents have been placed into this stream. (If a mapped stream is null an empty stream is used, instead.)

  • map takes a functional interface instance mapper and, according to it, maps each element to a new element, returning a new stream containing the mapped elements

  • flatMap takes a functional interface instance mapper that maps each element to a stream, then flattens the mapped streams, joining multiple streams into one and returning the concatenated stream

// mapimport java.util.Arrays;import java.util.List;public class Main {    public static void main(String[] args) {        List<String> nameList = Arrays.asList("Valerie" "Tom");        nameList.stream()            .map(String::toUpperCase)            .map(name -> "Hello, " + name)            .forEach(System.out::println);        nameList.stream()            .map(String::toUpperCase)            .flatMap((String name) -> Arrays.stream(name.split("")))            .forEach(System.out::println);    }}[OUTPUT]Hello, VALERIEHello, JACKHello, JOHNHello, TOMHello, JERRYHello, JESUS// flatMapimport java.util.Arrays;import java.util.List;import java.util.stream.Collectors;public class Main { public static void main(String[] args) {      // 给定两个数字列表 获取所有的数对        List<Integer> numbers1 = Arrays.asList(1 2 3);        List<Integer> numbers2 = Arrays.asList(3 4);        // numbers2.stream().map(y -> new int[] { x, y })返回Stream<int[]>,flatMap将每个x对应的Stream<int[]>连接        List<int[]> pairs = numbers1.stream().flatMap(x -> numbers2.stream().map(y -> new int[] { x, y }))                .collect(Collectors.toList());        for (int[] pair : pairs) {            System.out.print(Arrays.toString(pair));        } }}[OUTPUT][1 3][1 4][2 3][2 4][3 3][3 4]

reduce

reduce, as the name suggests, shrinks a stream down to a single value. It can compute sums, products, and minimum/maximum values over a collection.

  • Optional<T> reduce(BinaryOperator<T> accumulator)
    No initial value; returns an Optional. The parameter is a BinaryOperator functional interface instance used to combine the stream’s elements pairwise.
    Performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
    Parameters:

    • accumulator - an associative, non-interfering, stateless function for combining two values

    Returns:
    an Optional describing the result of the reduction

  • T reduce(T identity, BinaryOperator<T> accumulator)
    With an initial value; the return type matches the initial value’s type. The parameters are the initial value and a BinaryOperator functional interface instance

  • <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

    Parameters:

    • identity the identity value for the combiner function
    • accumulator an associative, non-interfering, stateless function for incorporating an additional element into a result
    • combiner an associative, non-interfering, stateless function for combining two values, which must be compatible with the accumulator function

    Returns:
    the result of the reduction


Associative: the order in which the accumulator function combines multiple values does not affect the final result. No matter how the values are grouped, the accumulator always produces the same result. Addition and multiplication, for example, are both associative operations.
Non-interfering: the accumulator function does not change the state of the values it processes. It simply combines them without affecting the original values.
Stateless: the accumulator function neither depends on nor modifies any external state. It computes its result purely from the arguments passed in, without involving any external, mutable, or global state.

// 求所有员工的工资之和、最高工资// 求工资之和方法1:Stream<Person> -> Stream<Integer> -> Optional<Integer>Optional<Integer> sumSalary =                personList.stream().map(Person::getSalary).reduce(Integer::sum);// 求工资之和方法2:Integer sumSalary2 = personList.stream().reduce(0 (sum, p) -> sum += p.getSalary()                Integer::sum);// 求最高工资方法1:Stream<Person> -> Stream<Integer> -> IntegerInteger maxSalary1 =                personList.stream().map(Person::getSalary).reduce(Integer::max).get();// 求最高工资方法2:Integer maxSalary2 = personList.stream().reduce(0                (max, p) -> max > p.getSalary() ? max : p.getSalary()                (max1, max2) -> max1 > max2 ? max1 : max2);// 求最高工资方法3:Integer maxSalary3 = personList.stream().reduce(0                                (max, p) -> max > p.getSalary() ? max : p.getSalary()                                Integer::max);

collect

The stream() method converts collections and arrays into a Stream, while collect() converts a Stream back into a collection.

String[] strArr = list.stream().toArray(String[]::new);List<Integer> strLengthList = list.stream().map(String::length).collect(Collectors.toList());List<String> strList = list.stream().collect(Collectors.toCollection(ArrayList::new));

count, max, min

The following methods are all terminal operations.

  • long count()
    Returns the count of elements in this stream.
  • Optional<T> max(Comparator<? super T> comparator)
    Returns the maximum element of this stream according to the provided Comparator.
  • Optional<T> min(Comparator<? super T> comparator)
    Returns the minimum element of this stream according to the provided Comparator.

Ref

Stream iterate
oracle docs
Java8 新特性之 Stream 流(含具体案例)
由浅入深体验 Stream 流(附带教程)
Java 8 Stream 和 Optional: 实践指南
Reducing a Stream