An overview,

Stream is the key abstraction for dealing with collections in Java8. It can specify what you want to do with collections, and can perform very complex operations like finding, filtering, and mapping data. Manipulating collection data using the Stream API is similar to database queries executed using SQL. You can also use the Stream API to perform operations in parallel. In short, the Stream API provides an efficient and easy-to-use way to process data.

Features:

  1. It’s not a data structure, it doesn’t hold data.
  2. Instead of modifying the original data source, it saves the manipulated data to another object. (Reservation: After all, the PEEK method can modify elements in the stream.)
  3. Lazy evaluation, in which the flow records the operation during intermediate processing and does not perform the actual calculation until the termination operation is performed.

Second, the classification of

Stateless: the processing of an element is not affected by previous elements; Stateful: The operation cannot continue until all elements are retrieved. Non-short-circuit operation: all elements must be processed to get the final result;

Short circuit operation: refers to meet certain eligible elements can get the final result, such as A | | B, if A is true, you do not need to judge the result of B.

Three, specific usage

A common way to create a stream

Use the stream() and parallelStream() methods under Collection

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); // Get a sequential stream
Stream<String> parallelStream = list.parallelStream(); // Get a parallel stream
Copy the code

Turn Arrays into streams using the stream() method of Arrays

Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
Copy the code

Use the static methods in Stream: of(), iterate(), generate()

Stream<Integer> stream = Stream.of(1.2.3.4.5.6);
 
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
 
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
Copy the code

Turn each line into a stream using the bufferedreader.lines () method

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
Copy the code

The string is separated into streams using the pattern.splitasstream () method

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
Copy the code

An intermediate operation of a stream

Screening and sectioning

Skip (n) : Skip n elements, with limit(n) to achieve paging distinct: Remove duplicate elements by hashCode() and equals() of elements in the stream

Stream<Integer> stream = Stream.of(6.4.6.7.3.9.8.10.12.14.14);
 
Stream<Integer> newStream = stream.filter(s -> s > 5) //6 6 7 8 10 12 14 14
        .distinct() //6 7 9 8 10 12 14
        .skip(2) //9 8 10 12 14
        .limit(2); / / September 8
newStream.forEach(System.out::println);
Copy the code

mapping

Map: Takes as an argument a function that is applied to each element and maps it to a new element. FlatMap: Takes a function as an argument, replaces each value in the stream with another stream, and joins all streams into one stream.

List<String> list = Arrays.asList("a,b,c"."1, 2, 3");
 
// Convert each element to a new element without commas
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",".""));
s1.forEach(System.out::println); // abc 123
 
Stream<String> s3 = list.stream().flatMap(s -> {
    // Convert each element to a stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
});
s3.forEach(System.out::println); // a b c 1 2 3
Copy the code

The sorting

Sorted () : sorted by nature. The sorted(Comparator com) interface is implemented with the Comparable interface

List<String> list = Arrays.asList("aa"."ff"."dd");
// The String class itself implements the Compareable interface
list.stream().sorted().forEach(System.out::println);// aa dd ff
 
Student s1 = new Student("aa".10);
Student s2 = new Student("bb".20);
Student s3 = new Student("aa".30);
Student s4 = new Student("dd".40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
 
// Custom sort: ascending by name first, ascending by age if name is the same
studentList.stream().sorted(
        (o1, o2) -> {
            if (o1.getName().equals(o2.getName())) {
                return o1.getAge() - o2.getAge();
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        }
).forEach(System.out::println);
Copy the code

consumption

Peek: Like a map, you can get every element in the stream. But the map receives a Function expression that returns a value; Peek receives the Consumer expression and returns no value.

Student s1 = new Student("aa".10);
Student s2 = new Student("bb".20);
List<Student> studentList = Arrays.asList(s1, s2);
 
studentList.stream()
        .peek(o -> o.setAge(100))
        .forEach(System.out::println);   
 
/ / the result:
Student{name='aa', age=100}
Student{name='bb', age=100}
Copy the code

The termination operation of a stream

Match and aggregate operations

AllMatch: Receives a Predicate function that returns true only if each element in the stream matches the Predicate, or false noneMatch otherwise: Predicate receives a Predicate function, which returns true only if every element in the stream does not satisfy the Predicate, false anyMatch otherwise: Receives a Predicate function, which returns true only if one element in the stream satisfies the Predicate, and false findFirst otherwise: FindAny: any element in the stream count: total number of elements in the stream Max: maximum number of elements in the stream min: minimum number of elements in the stream

List<Integer> list = Arrays.asList(1.2.3.4.5);
 
boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true
 
Integer findFirst = list.stream().findFirst().get(); / / 1
Integer findAny = list.stream().findAny().get(); / / 1
 
long count = list.stream().count(); / / 5
Integer max = list.stream().max(Integer::compareTo).get(); / / 5
Integer min = list.stream().min(Integer::compareTo).get(); / / 1
Copy the code

Statute operation

Optional < T > reduce (BinaryOperator < T > accumulator) :

When first executed, the first parameter of the accumulator function is the first element in the stream and the second parameter is the second element of the element in the stream. On the second execution, the first argument is the result of the first function execution and the second argument is the third element in the stream. And so on.

T Reduce (T identity, BinaryOperator<T> Accumulator) :

The flow is the same as above, except that on the first execution, the accumulator function takes identity as its first argument and is the first element in the flow as its second argument.

<U> U reduce(U identity,BiFunction<U, ? Super T, U > accumulator, BinaryOperator < U > combiner) :

In a stream, this method is the same as the second method, that is, the third parameter combiner does not work. In parallelStream, we know that the stream is forked and joined by multiple threads. In this case, the execution flow of each thread is the same as that of the second reduce(Identity, Accumulator) method. The third parameter combiner function, The execution result of each thread is treated as a new flow, and the first method reduce(Accumulator) process is used for specification.

When the number of elements is less than 24, the number of parallel threads is equal to the number of elements. When the number of elements is greater than or equal to 24, the number of parallel threads is 16

List<Integer> list = Arrays.asList(1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24);
 
Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get();
System.out.println(v);   / / 300
 
Integer v1 = list.stream().reduce(10, (x1, x2) -> x1 + x2);
System.out.println(v1);  / / 310
 
Integer v2 = list.stream().reduce(0,
        (x1, x2) -> {
            System.out.println("stream accumulator: x1:" + x1 + " x2:" + x2);
            return x1 - x2;
        },
        (x1, x2) -> {
            System.out.println("stream combiner: x1:" + x1 + " x2:" + x2);
            return x1 * x2;
        });
System.out.println(v2); / / - 300
 
Integer v3 = list.parallelStream().reduce(0,
        (x1, x2) -> {
            System.out.println("parallelStream accumulator: x1:" + x1 + " x2:" + x2);
            return x1 - x2;
        },
        (x1, x2) -> {
            System.out.println("parallelStream combiner: x1:" + x1 + " x2:" + x2);
            return x1 * x2;
        });
System.out.println(v3); / / 197474048
Copy the code

Collect operation

Collect: Receive an instance of Collector to collect elements from the stream into another data structure. Collector

is an interface with the following five abstract methods:
,>

Supplier Supplier () : Creates A result container, A

BiConsumer

Accumulator () : Consumer interface with container A as the first parameter and element T in the stream as the second parameter.
,>

BinaryOperator combiner() : function interface. This parameter is the same as combiner parameter in reduce. It combines the running results of each child process in the parallel flow (accumulator container A after the accumulator function operation).

Function

finisher() : Function interface, parameter: container A, return type: collect the final result R.
,>

Set

Characteristics () : Returns an immutable Set Set that identifies the Characteristics of the Collector.

It has the following characteristics: CONCURRENT: Indicates that the collector supports concurrency. (There are other descriptions in the official documentation, so I haven’t explored them yet, so I won’t translate them too much.)

UNORDERED: Indicates that the collection operation does not preserve the original order of elements in the flow.

IDENTITY_FINISH: indicates that the finisher parameter is only an identifier and can be ignored.

Collector Tool library: Collectors

Student s1 = new Student("aa".10.1);
Student s2 = new Student("bb".20.2);
Student s3 = new Student("cc".10.3);
List<Student> list = Arrays.asList(s1, s2, s3);
 
/ / put into the list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); / / [10, 20, 10]
 
/ / into the set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); / / [20, 10]
 
// Convert to map. Note: The keys must be different; otherwise, an error will be reported
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10}
 
// String delimiter connection
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(","."(".")")); // (aa,bb,cc)
 
// Aggregate operations
//1
Long count = list.stream().collect(Collectors.counting()); / / 3
//2. Maximum age (same with minBy)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); / / 20
//3. Age of everyone
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); / / 40
//4. Average age
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); / / 13.333333333333334
// Bring all of the above methods
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
 
/ / group
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
// Multiple groups, first by type and then by age
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));
 
/ / partition
// Divided into two parts, one is over 10 years old, the other is less than or equal to 10 years old
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
 
/ / code
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); / / 40
3.32.Collectors. ToList () parsing/ / toList source code
public static<T> Collector<T, ? , List<T>> toList() {return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
            (left, right) -> {
                left.addAll(right);
                return left;
            }, CH_ID);
}
 
// To better understand, let's convert the source code to lambda expressions
public<T> Collector<T, ? , List<T>> toList() { Supplier<List<T>> supplier = () ->new ArrayList();
    BiConsumer<List<T>, T> accumulator = (list, t) -> list.add(t);
    BinaryOperator<List<T>> combiner = (list1, list2) -> {
        list1.addAll(list2);
        return list1;
    };
    Function<List<T>, List<T>> finisher = (list) -> list;
    Set<Collector.Characteristics> characteristics = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
 
    return new Collector<T, List<T>, List<T>>() {
        @Override
        public Supplier supplier(a) {
            return supplier;
        }
 
        @Override
        public BiConsumer accumulator(a) {
            return accumulator;
        }
 
        @Override
        public BinaryOperator combiner(a) {
            return combiner;
        }
 
        @Override
        public Function finisher(a) {
            return finisher;
        }
 
        @Override
        public Set<Characteristics> characteristics(a) {
            returncharacteristics; }}; }Copy the code

end