Stream learning notes

Introduction to Stream

  • Stream is neither a collection nor a data structure. It cannot save data

  • Stream is somewhat similar to the advanced Iterator and can be used for algorithm and calculation

  • Unlike iterators, streams can operate in parallel. Data is divided into many segments and processed in different threads

  • Data source, zero or more intermediate operations, and zero or one terminal operation

  • All intermediate operations are inert and will not have any effect until the pipeline starts working

  • The terminal operation is a bit like a faucet. When the faucet is turned on, the water will flow and the intermediate operation will be executed

1, How to create a street? [Street data source]

The data source is an array

stream = Stream.of("1", "2", "3");
//Equivalent to
String[] arr = new String[]{"1", "2", "3"};
Stream<String> stream = Arrays.stream(arr);

Data sources are collections

//Set list, AP set
List<String> list = new ArrayList<>();
        list.add("I");
        list.add("yes");
        list.add("aggregate");
        Stream<String> stream  = list.stream(); //Sequential flow
		ParallelStream<String>	parallelStream =list.parallelStream(); //Parallel flow

Parallel streams can be created through parallelStream(). The default thread pool is ForkJoinPool instead of ThreadPoolTaskExecutor

2, How does Stream flow work? [intermediate operation and terminal operation]

A stream represents a collection of a series of elements that we can perform different types of operations on to perform calculations on these elements. It may sound a little awkward. Let's speak in Code:

List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
     myList
    .stream() // Create stream
    .filter(s -> s.startsWith("c")) // Perform filtering to filter out the string prefixed with c
    .map(String::toUpperCase) // Convert to uppercase
    .sorted() // sort
    .forEach(System.out::println); // for circular printing

// C1
// C2

We can perform intermediate operations or terminal operations on convection.

What is intermediate operation? What is terminal operation?

Stream intermediate operation, terminal operation

  • ① : the intermediate operation will return a stream again, so we can link multiple intermediate operations. Note that there is no semicolon here. filter filtering, map object conversion and sorted sorting in the above figure belong to intermediate operations.
  • ② : terminal operation is an end action of convection operation, which generally returns void or a non stream result. The forEach loop in the figure above is a termination operation.

After reading the above operation, do you feel like a pipeline operation.

In fact, most stream operations support lambda expressions as parameters. Correctly understood, it should be said to accept the implementation of a functional interface as parameters.

3, Different types of Stream streams

1. Distinguish according to execution scheduling

1.1 serial stream
1.2 parallel flow

The thread pool used by parallel streams in Stream is ForkJoinPool, which is characterized by tasks

@Test
public void case6(){
    List<Person> persons = Arrays.asList(
            new Person("Max", 18),
            new Person("Max", 18),
            new Person("Peter", 23),
            new Person("Pamela", 23),
            new Person("David", 12),
            new Person("David", 12),
            new Person("David", 12));
    //Directly create parallel stream (parallelstream)
    persons.parallelStream().peek((person)-> System.out.println(person.getName()+":"+Thread.currentThread().getName())).forEach(System.out::println);
    //First use serial and then parallel
    persons.stream().distinct().parallel().peek((person)-> System.out.println(person.getName()+":"+Thread.currentThread().getName())).forEach(System.out::println);
}

2. Distinguish by data type

2.1 basic data type stream (int,long,double)
  • IntStream
  • LongStream
  • DoubleStream
2.2 object flow
  • Stream

4, Intermediate operation

//First define an operation object, and then use
List<Person> persons = Arrays.asList(
            new Person("Max", 18),
            new Person("Max", 18),
            new Person("Peter", 23),
            new Person("Pamela", 23),
            new Person("David", 12));
1.map

Traversal elements for editing, with return values, can be type converted

@Test
public void testMap() {
    Stream.of("apple", "banana", "orange", "watermelon", "grape")
            .map(e -> {
                e = e.substring(1, 1);
                return e.length();
            }) //Length converted to word int
            .forEach(System.out::println); //output
}
2.mapToInt

Object flow basic data flow. Of course, these maps can do it. mapToLong and mapToDouble are similar to mapToInt

@Test
public void testMapToInt(){
    Stream.of("apple", "banana", "orange", "waltermaleon", "grape")
        .mapToInt(String::length) //Convert to int
        .forEach(System.out::println);
}
3.peek

Traverse elements for editing and printing information. There is no return value and type conversion is not allowed (in fact, it is provided to us to observe the elements of the flow)

@Test
public void tesPeek() {
    Stream.of( new StringBuilder("apple") ,new StringBuilder("banana"),new StringBuilder("orange"),new StringBuilder("watermelon"),new StringBuilder("grape"))
        .peek(s-> s.append("aa")) //Splicing aa
        .map(StringBuilder::toString)
        .forEach(System.out::println); //output
}
4.flatMap

The function of flatMap is to flatten and flatten the elements, reorganize the flattened elements into a Stream, and merge these streams into a Stream in series

//flatMap() parameter, the return value of the functional interface is also a Stream stream
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//map(), and the return value of the functional interface is Object
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
@Test
public void testFlatMap(){
    Stream.of("a-b-c-d","e-f-i-g-h")
            .flatMap(e->Stream.of(e.split("-")))
            .forEach(System.out::println);
}
5.distinct

De duplication will be judged according to the equals method. If you want to de duplicate your own custom bean s, you need to rewrite the equals method, but this is not the only method. You can also customize the filtering rules

@Test
public void testDistinct() {
    //The Persons object is de duplicated according to equals by default
    persons.stream()
            .distinct()
            .forEach(System.out::println);

    //Integer de duplication
    IntStream.of(1, 2, 2, 3)
            .distinct()
            .forEach(System.out::println);
}
6.Limit

Limit the number of elements

@Test
    public  void testLimit(){
        //Persons limit the number of elements
        persons.stream()
                .limit(3)
                .forEach(System.out::println);
        //Integer limits the number of elements
        Stream.of(1,2,3,4,5,6)
                .limit(3) //Limit three
                .forEach(System.out::println); //The first three 1, 2 and 3 will be output
    }
7.skip

Skip the first n elements

@Test
public void testSkip() {
    //Persons skips the first n elements
    persons.stream()
            .skip(3)
            .forEach(System.out::println);
    //Integer skips the first n elements
    Stream.of(1,2,3,4,5,6)
            .skip(3) //Limit three
            .forEach(System.out::println); //The first three 1, 2 and 3 will be output
}
8.sorted

The bottom layer of the sorting comparator depends on the Comparable implementation, and a user-defined comparator can also be provided

@Test
    public void testSorted() {
        ///People sorting. Note that the sorting rules are customized here
        persons.stream()
                .sorted(Comparator.comparingInt(Person::getAge).thenComparing(Person::getName))
                .forEach(System.out::println);

        //Integer sort
        Stream.of(1,2,4,3,5,6)
                .sorted()
                .forEach(System.out::println);
    }

#Intstream (unique method)

  • IntStream
  • LongStream
  • DoubleStream
1.mapToObj

Basic data flow object flow

@Test
public void testMapObject() {
    // Here, it is converted into a string object and starts with string + a
    IntStream.of(1, 2, 3)
        .mapToObj(String::valueOf)
        .map(s -> "a" + s)
        .forEach(System.out::println);
}
2.boxed

Automatic boxing, converting the basic type int to the object type Integer

@Test
public void testBoxed() {
    // Convert base type to object type
    IntStream.range(0, 10).boxed().forEach(System.out::println);
    //amount to
    IntStream.range(0, 10).mapToObj(Integer::valueOf).forEach(System.out::println);
}

5, Terminal operation

//Or the whole collection first
List<Person> persons = Arrays.asList(
            new Person("Max", 18),
            new Person("Max", 18),
            new Person("Peter", 23),
            new Person("Pamela", 23),
            new Person("David", 12));
1.forEach

forEach is not only the terminal operation of Stream, but also the syntax sugar of collection

@Test
public void testForEach() {
    persons.forEach(System.out::println);
}
2.count

Find set length

@Test
public void testCount() {
    long count =  persons.stream().count();
    System.out.println(count);
}
3.findFirst

Gets the first element in the stream

@Test
public void testFindFirst() {
    Optional<Person> optional = persons.stream().findFirst();
    Person person = optional.orElse(null);
    System.out.println(person);
}
4.findAny

Get any element in the stream

@Test
public void testFindAny() {
    Optional<Person> optional = persons.stream().findAny();
    Person person = optional.orElse(null);
    System.out.println(person);
}
5.max

Find the maximum value and take the last one according to the sorting rules

@Test
public void testMax() {
    Optional<Person> max1 = persons.stream().max((p1, p2) -> {
        return p1.getAge().compareTo(p2.getAge());
    });
    Person person1 = max1.orElse(null);
    System.out.println(person1);
	//simplify
    Optional<Person> max2 = persons.stream().max(Comparator.comparing(Person::getAge));
    Person person2 = max2.orElse(null);
    System.out.println(person2);

}
6.min

Find the minimum value and take the first one according to the sorting rules

@Test
public void testMin() {
    Optional<Person> min1 = persons.stream().min((p1, p2) -> {
        return p2.getAge().compareTo(p1.getAge());
    });
    Person person1 = min1.orElse(null);
    System.out.println(person1);
	//simplify
    Optional<Person> min2 = persons.stream().min(Comparator.comparing(Person::getAge));
    Person person2 = min1.orElse(null);
    System.out.println(person2);

}
7.toArray

Turn array

@Test
public void testToArray() {
    Object[] objects = persons.stream().toArray();
    Arrays.stream(objects).forEach(System.out::println);
}
8.reduce

Merge

	The data in the convection calculates a result according to the calculation method you specify(Reduction operation)
	reduce The function of is to Stream Combine elements in,We can pass in an initial value,It will take the elements in the stream in turn according to our calculation method and calculate on the basis of the basic value,The calculation results are calculated in the following elements
	//Find the value of the maximum age
    //You can use max (actually based on reduce) or reduce
    //reduce sum, difference set and everything. In fact, it is the continuous calculation of a group of data
    @Test
    public void case5(){
        List<Person> persons = Arrays.asList(
                new Person("Max", 18),
                new Person("Max", 18),
                new Person("Peter", 23),
                new Person("Pamela", 23),
                new Person("David", 12),
                new Person("David", 12),
                new Person("David", 12));
        //Find the minimum age
        int minAge = persons.stream().mapToInt(Person::getAge).reduce(Integer.MAX_VALUE,new IntBinaryOperator(){
            @Override
            public int applyAsInt(int left, int right) {
                return left > right?right:left;
            }
        });
        System.out.println(minAge);
        //Find the maximum age, abbreviation
        int maxAge = persons.stream().mapToInt(Person::getAge).reduce(Integer.MIN_VALUE, Math::max);
        System.out.println(maxAge);

    }
9. Collect (most important)

Here is only a brief introduction to the collection function, which is also the most commonly used. Please refer to this article https://www.jianshu.com/p/6ee7e4cd5314

1.toList and toSet

@Test
public void testToListSet(){
    List<Person> persons = Arrays.asList(
            new Person("Max", 1),
            new Person("Max", 2),
            new Person("Peter", 3),
            new Person("Pamela", 4),
            new Person("David", 5));
    //toList
    List<Person> list1 = persons.stream().distinct().collect(Collectors.toList());
    list1.forEach(System.out::println);
    //toSet
    Set<Person> set = persons.stream().collect(Collectors.toSet());
    set.forEach(System.out::println);
}

2.toMap

    @Test
    public void testToMap(){
        List<Person> persons = Arrays.asList(
                new Person("Max", 1),
                new Person("Max", 2),
                new Person("Peter", 3),
                new Person("Pamela", 4),
                new Person("David", 5));
        //key value can be the object itself (represented by Function.identity()) or an attribute
        Map<Person, Person> collect1 = persons.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
        collect1.forEach((k,v)->{
            System.out.println(k+":"+v);
        });
        Map<Integer, String> collect2 = persons.stream().collect(Collectors.toMap(Person::getAge, Function.identity()));
        collect2.forEach((k,v)->{
            System.out.println(k+":"+v);
        });
    }

3.groupingBy

	//There are three overloaded methods
	//groupingBy(Function)
	//groupingBy(Function , Collector)
    //groupingBy(Function, Supplier, Collector)
    //Function custom grouping rules
    //Supplier defines the type of Map. The default is HashMap::new
    //Collector s customize the format of each group. The default is collectors toList()
    @Test
    public void testGroup(){
        List<Person> persons = Arrays.asList(
                new Person("Max", 1),
                new Person("Min", 1),
                new Person("Peter", 2),
                new Person("Pamela", 3),
                new Person("David", 4));
        //1. Grouping by age
        System.out.println("divide into groups according to age");
        ConcurrentHashMap<Integer, Set<Person>> map1 = persons.stream().collect(Collectors.groupingBy(Person::getAge, ConcurrentHashMap::new, Collectors.toSet()));
        map1.forEach((k,v)->{
            System.out.println(k+":"+v);
        });

        //2. User defined grouping rules
        System.out.println("Custom grouping rules");
        Map<String, List<Person>> map2 = persons.stream().collect(Collectors.groupingBy((person -> {
            if (person.getAge().equals(1)){
                return "Equal to 1";
            }
            if (person.getAge().equals(2) || person.getAge().equals(3)){
                return "Equal to 2 or 3";
            }
            if (person.getAge().equals(4)){
                return "Equal to 4";
            }
            return null;
        } )));
        map2.forEach((k,v)->{
            System.out.println(k+":"+v);
        });

        //3.partitioningBy partition (can only be divided into two groups)
        //Judge whether the age is 1
        Map<Boolean, List<Person>> map3 = persons.stream().collect(Collectors.partitioningBy(person -> person.getAge().equals(1)));
        map3.forEach((k,v)->{
            System.out.println(k+":"+v);
        });
    }

4.Collectors.joining()

//Splicing strings, there are three overloaded methods
//Collectors.joining()
//Collectors.joining(CharSequence delimiter)
//Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)
//Delimiter is a delimiter, which defaults to ''
//Prefix is a prefix, and the default is' '
//Suffix is the suffix, and the default is ""
@Test
public void  testJoining(){
    List<Person> persons = Arrays.asList(
            new Person("Max", 1),
            new Person("Peter", 2),
            new Person("Pamela", 3),
            new Person("David", 4));
    String s1 = persons.stream().map(Person::getName).collect(Collectors.joining());
    String s2 = persons.stream().map(Person::getName).collect(Collectors.joining(","));
    String s3 = persons.stream().map(Person::getName).collect(Collectors.joining(",","name:",";"));
    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);

}

#Intstream (unique method)

  • IntStream
  • LongStream
  • DoubleStream
1.Reduce

Merge operation

//reduce has two overloaded methods
//1. The parameters are the starting value and operation rules. The return value is int. there are starting values and operation rules. The two parameters have the same return type and the starting value type.
T reduce(T identity, BinaryOperator<T> accumulator)  
//2. The parameter is an operation rule, and the return value is Optional
OptionalInt reduce(IntBinaryOperator op);
@Test
public void testReduce() {
    //There are starting values and operation rules
    int sum = IntStream.range(0, 10).reduce(0, (v1, v2) -> v1 - v2);
    System.out.println(sum);
    // The protocol operation returns optionalInt, which does not necessarily have a value
    OptionalInt reduce = IntStream.of().reduce((v1, v2) -> v1/v2);
    reduce.ifPresent(System.out::println);
}
2.sum

Sum

@Test
    public void testSum() {
        int sum = IntStream.rangeClosed(0, 10).sum();
        System.out.println(sum);
    }
3.average

Average

@Test
public void testAverage() {
    OptionalDouble optionalDouble = IntStream.of(-2, 2, -9, 10, 9).average();
    double average = optionalDouble.getAsDouble();
    System.out.println(average);
}

6, Precautions

  • Lazy evaluation (if there is no termination operation, only intermediate operation will not be executed)
  • A flow is one-time (once a flow passes through a termination operation, the flow can no longer be used)
  • The operation of convection will not affect the original data

Stateless: refers to that the processing of elements is not affected by previous elements;

Stateful: this means that the operation cannot continue until all elements are obtained.

Non short circuit operation: it means that all elements must be processed to obtain the final result;

Short circuit operation: it means that the final result can be obtained when meeting some qualified elements, such as A | B. as long as A is true, there is no need to judge the result of B.

7, Optional

Optional is to solve the bloated problem of null pointer verification

1. Create Optional

Optional.ofNullable(author) is recommended

Optional.of(author) if author==null, null pointer will be reported

public class OptionalDemo {
    public static void main(String[] args) {
        //When it is not known whether the author is null, in order to prevent null pointer exceptions
        Author author = OptionalDemo.getAuthor();
        //1. In the past, we did this for nothing
        if (author != null) {
            System.out.println(author.getAge());
        }
        //After 2.jdk8, this object can be wrapped as an optional < author > object
        Optional<Author> optionalAuthor = Optional.ofNullable(author);
        System.out.println("optionalAuthor:" + optionalAuthor);
        //Then judge
        optionalAuthor.ifPresent(author1 -> System.out.println(author1.getName()));
        //3. Pack the Optional in the method
        Optional<Author> optionalAuthor2 = OptionalDemo.getOptionalAuthor();
        System.out.println("optionalAuthor2:" + optionalAuthor2);
        optionalAuthor2.ifPresent(author1 -> System.out.println(author1.getName()));
    }

    public static Author getAuthor() {
        Author author = new Author();
        author.setAge(18);
        author.setName("Zhao Yun");
       // return null;
        return author;
    }

    public static Optional<Author> getOptionalAuthor() {
        Author author = new Author();
        author.setAge(20);
        author.setName("Fei Zhang");
        author =  null;
        //return Optional.of(author); // The incoming object cannot be empty
        return Optional.ofNullable(author);
    }
}
2. Safe consumption

ifPresent()

public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
3. Get object

1. Get (not recommended, null object with NoSuchElementException)

Author author1 = optionalAuthor2.get();

2. Orelseget (if the object is null, customize the returned object)

optionalAuthor2.orElseGet(new Supplier<Author>() {
            @Override
            public Author get() {
                Author author2 = new Author();
                author2.setAge(18);
                author2.setName("Cao Cao");
                return author2;
            }
        });

3. Orelsethrow (throw self-defined exception if the object is null)

optionalAuthor2.orElseThrow(new Supplier() {
            @Override
            public Exception get() {
                return  new Exception("holy crap");
            }
        });
//simplify
optionalAuthor2.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("holy crap"));
4. Filtration

The filter judges whether the object meets the standard. If it does not, it will be regarded as null

public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
5. Judgment

ifPresent determines whether the object is empty. If it is empty, it returns false, and if it is not empty, it returns true

 public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
6. Data conversion

map performs type conversion on data, which is the same as that in Stream

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
//The return value is also an Optional object
Optional<String> optionalName = optionalAuthor2.map(Author::getName);

icate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}

##### 5. Judgment

**ifPresent Judge whether the object is empty,Null return false,Not null return true**

```java
 public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
6. Data conversion

map performs type conversion on data, which is the same as that in Stream

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
//The return value is also an Optional object
Optional<String> optionalName = optionalAuthor2.map(Author::getName);

Keywords: Java stream

Added by bagpuss03 on Tue, 25 Jan 2022 17:37:33 +0200