Java Stream
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

Methods
| Method | Purpose | Operation type |
|---|---|---|
count | Count elements | terminal |
forEach | Iterate and process | terminal |
reduce | Reduce | terminal |
collect | Collect | terminal |
findAny | Find | terminal |
anyMatch | Match | terminal |
filter | Filter | intermediate |
sorted | Sort | intermediate |
distinct | Deduplicate | intermediate |
limit | Take the first few | intermediate |
skip | Skip the first few | intermediate |
map | Map | intermediate |
concat | Concatenate | intermediate |
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 exampleSystem.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 conditionboolean allMatch(Predicate<? super T> predicate)
Returns true only if all elements match the given conditionboolean 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.)maptakes a functional interface instance mapper and, according to it, maps each element to a new element, returning a new stream containing the mapped elementsflatMaptakes 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 reductionT 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:
identitythe identity value for the combiner functionaccumulatoran associative, non-interfering, stateless function for incorporating an additional element into a resultcombineran 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

