[Java] Introduction to Stream API

What is Stream API #?

It's like a continuation of the article here.

Stream API is an API for processing Data in PipeLine format. Extract individual elements from a data source such as a collection, array, or file It provides a mechanism for passing this to the "process flow" (Stream).

"Intermediate operation" that returns the result of performing a function operation on Stream as Stream There is a "termination operation" that returns the processing result as Data.

Since many method arguments take a functional interface for both intermediate and termination operations. If the knowledge of the lambda expression is used here, it will be smart. Stream API.png

Stream ...? Is it different from I / O Stream?

Java provides I / O Stream in the java.io package, The meaning of Stream here is a concept that compares input and output to Stream. Stream of Stream API is a concept that makes PipeLine processing of Data like Stream.

Stream API basics


import java.util.ArrayList;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {

        //① Prepare Data Source
        var list = new ArrayList<String>(
        		Arrays.asList("tokyo", "nagoya", "osaka", "fukuoka", "hokkaido", "okinawa"));

        //② Make a stream
            filter(s -> s.length() > 5).         //③ Perform intermediate processing
            forEach(System.out::println);        //④ Perform termination processing


Stream API processing consists of the following. ** ① Prepare Data Source → ② Stream generation → ③ Intermediate processing such as extraction / processing → ④ Termination processing such as output / aggregation **

In the above example, ① First, create a Data Source for ArrayList

(2) Create a Stream. Since it is based on ArrayList \ here The Stream method also returns a Stream \ object.

③ Use the filter method to "extract only values with more than 5 characters" "Convert to uppercase" with map method There may be multiple intermediate processes. You can omit it.

(4) Output the obtained value with the System.out :: println method with the forEach method. ** Termination processing cannot be omitted. ** **   The return value of the intermediate processing is Stream \ . In the Stream API, the "." Operator is used to perform the process from Stream generation to intermediate processing / termination processing. You can connect them all together and write smartly. (Called ** method chain ** in the sense of method chain)

A series of Stream processing is executed at the timing of termination processing. Even if operations such as extraction / processing are called in the middle, they are once stocked and It is not executed on the spot and waits for processing until termination processing. This is called ** delay processing **.

How to make a Stream

Generated from Collection / Array

** From Collection ** Collection.stream() ** From array ** Arrays.stream(T[]) ** From Map ** Map.entrySet().stream()

There is also a parallelStream () method as a parallel version of the stream () method. Parallel processing is possible just by replacing stream with parallelStream. (strong…) If the number of elements to be handled is large, it may be possible to process efficiently by enabling parallel processing. (Of course, it is not always fast because of the overhead of parallelization.) You can also parallelize or serialize an existing Stream.

Stream generation from Stream class

In Stream class, there is Factory Method for Stream generation. The most basic is the of method, which converts the specified variadic argument to a Stream.

var stream = Stream.of("tokyo","osaka","nagoya");
stream.forEach(System.out::println); // tokyo, osaka, nagoya

There are also generate (), builder (), concat () and iterator (). I will omit it here.

Primitive type Stream generation

IntStream stream specialized with int LongStream Stream specialized with long DoubleStream Stream specialized with double

IntStream.range (int start, int endExclusive) [Second argument is out of range: open space] IntStream.rangeClosed (int start, int endInclusive) [Second argument is within range: closed space]

An example of iterative processing using IntStream is as follows. Compare with the case of using the for statement. It's a little fashionable.

Repeat using for statement

for(int i = 1; i <= 5 ; i++){

Repeat using IntStream

IntStream.rangeClosed(1, 5).forEach(System.out::println);

Primitive types cannot be used in Java generics type arguments, so Writing like Stream \ will result in an error.

Intermediate processing

It has the role of extracting / processing the values flowing in the Stream. The intermediate processing is executed only when the termination processing is called. It is not executed every time it is called. filter Extracts the value under the specified conditions.

Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).forEach(System.out::println); //tokyo

map Process the given value.

Stream.of("tokyo", "nagoya", "osaka").map(s -> s.length)).forEach(System.out::println); //5, 6, 5

Note that it was Stream \ immediately after it was created, but it is now Stream \ after the map method.

sorted Sort the elements.

Stream.of("tokyo", "nagoya", "osaka").sorted().forEach(System.out::println); // nagoya, osaka, tokyo
Stream.of(2,1,3).sorted().forEach(System.out::println); // 1, 2, 3

The default behavior is sorting in natural order. If it is a character string, it is sorted in dictionary order, and if it is a numerical value, it is sorted by large or small. If you want to specify your own sort rule, set the sort rule with a lambda expression. The argument of sorted () is the Comparator interface.

Stream.of("tokyo", "nagoya", "osaka").
  sorted((str1, str2) -> str1.length() - str2.length()).forEach(System.out::println); // tokyo, osaka, nagoya

skip/limit skip: Skip elements up to m limit: truncate n + 1 and subsequent elements

IntStream.range(1, 10).skip(3).limit(5).forEach(System.out::println); // 4, 5, 6, 7, 8

Skip the first 4 elements with the skip method The limit method extracts 5 elements from it. Note the arguments because the limit method operates on a Stream that has already been truncated.

peek Check the intermediate status of Stream. The peek method itself does not affect Stream, so it is mainly used for debugging.

Stream.of("tokyo", "nagoya", "osaka").peek(System.out::println).sorted().forEach(System.out::println);
//Results before sorting: tokyo, nagoya,osaka ← peek println
//Results after sorting: nagoya, osaka,tokyo ← for Each println

distinct Remove duplicate values.

Stream.of("tokyo", "nagoya", "osaka", "osaka", "nagoya", "tokyo").distinct().forEach(System.out::println);
// tokyo, nagoya, osaka


It has the role of finally outputting / aggregating the values flowing in the Stream. Since Stream is finally processed collectively with the call of termination processing as a trigger. Unlike the intermediate processing, the termination processing cannot be omitted.

Because the terminated Stream cannot be reused If you want to perform Stream processing again, you need to regenerate the Stream itself from the Data Source.

forEach Process the individual elements in order.

Stream.of("tokyo", "nagoya", "osaka").forEach(v -> System.out.println(v)); // tokyo, nagoya, osaka
Stream.of("tokyo", "nagoya", "osaka").forEach(System.out::println); // tokyo, nagoya, osaka

findFirst Get the first value.

System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).findFirst().orElse("empty"));
// tokyo

The return value of the findFirst method is Optional because it may be an empty Stream.

If empty

System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("a")).findFirst().orElse("empty"));
// empty

anyMatch/allMatch/noneMatch Determines if the value meets a particular condition. In order, "whether there is an element whose conditional expression is true", "whether all conditional expressions are true", "Isn't all the conditional expressions true?"

System.out.println(Stream.of("tokyo", "nagoya", "osaka").anyMatch(v -> v.length() == 5)); // true
System.out.println(Stream.of("tokyo", "nagoya", "osaka").allMatch(v -> v.length() == 5)); // false
System.out.println(Stream.of("tokyo", "nagoya", "osaka").noneMatch(v -> v.length() == 5)); // false

toArray Converts the result of Stream processing as a character string array.

var list = Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).toArray();

collect (collection conversion)

Converts the result of Stream processing as Collection. Pass the conversion method provided by the Collectors class to the collect method. Use toList for conversion to List, toSet for conversion to Set, and toMap for conversion to map.

var list = Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).collect(Collectors.toList());

The collect method is not a method dedicated to collection conversion It is also a method that performs reduction processing. In the case of reduction processing, it will be described later.

min/max Find the minimum / maximum value. It is necessary to specify a comparison rule (Comparator) as an argument. Since the return value is Optional type, it will be via orElse. (It is -1 which means that it is not here.)

System.out.println(Stream.of(1, 3, 2).min((int1, int2) -> int1 - int2).orElse(-1)); // 1
System.out.println(Stream.of(1, 3, 2).min((int1, int2) -> int2 - int1).orElse(-1)); // 3
System.out.println(Stream.of(8, 7, 9).max((int1, int2) -> int1 - int2).orElse(-1)); // 9
System.out.println(Stream.of(8, 7, 9).max((int1, int2) -> int2 - int1).orElse(-1)); // 7

count Find the number of elements.

System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.length() > 5).count()); // 1

reduce Combine Stream values into one (Reduction). The reduce method provides three types of overloads.

With one argument

Optional reduce(BinaryOperator accumulator); Since the return value is Optional type, it will be via orElse.

The arguments are the variable result for storing the operation result and the variable str for receiving individual elements.

With one argument

    Stream.of("tokyo", "nagoya", "osaka").sorted()
        .reduce((result, str) -> { return result + "," + str;}).orElse("")); // nagoya,osaka,tokyo

With 2 arguments

T reduce(T identity, BinaryOperator accumulator); You can receive the initial value with the first argument. The result is obviously non-null, so it is of non-Optional type. No need to go through OrElse.

With 2 arguments

    Stream.of("tokyo", "nagoya", "osaka").sorted()
        .reduce("hokkaido",  (result, str) -> { return result + "," + str;})); //hokkaido,nagoya,osaka,tokyo

With 3 arguments

U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator combiner); It may be a little difficult. It is used when the element type of Stream and the final element type are different. An example is omitted here. If you want to know more details, you may want to see the article here.

collect (reduction operation)

Collect the elements in Stream into Collection etc. While reduce reduces the elements in Stream to a single value such as int or String, collect accumulates and returns values for a variable container such as Collection / StringBuilder (variable reduction).

This is a little esoteric, so I'll update it later ...

At the end

I plan to get Java Gold SE 11 by March. (In the first place, you have to take Java Silver SE 11 as a premise ...) In Gold, the question rate of lambda expression / Stream API is very high, so I organized it.

Java SE 11 is a qualification that was just completed about half a year ago (late 2019/06). Until now, SE 8 was the latest version. A reference book for Silver SE 11 has already been published, Gold SE 11 has not been published as a reference book at this time (December 2019). Unlike the SE 8 test, the SE 11 test uses the lambda expression / Stream API. I think it will be based on SE 11, so please be careful if you take the test.

Recommended Posts