04-Java8 new feature Stream API

Posted by adv on Tue, 14 Dec 2021 13:40:45 +0100

brief introduction

Two of the most important changes in Java 8 are Lambda expressions and the Stream API(java.util.stream. *)

Stream is a key abstract concept for processing collections in Java 8. It can specify the operations you want to perform on collections, and can perform very complex operations such as finding, filtering and mapping data

Using Stream API to operate collection data is similar to using SQL to execute database queries. You can also use Stream API to perform operations in parallel. In short, Stream API provides an efficient and easy-to-use way to process data

What is Stream

What is a stream?

It is a data channel, which is used to operate the element sequence generated by the data source (set, array, etc.). "Set is about data, and flow is about calculation!"

be careful:

  • Stream itself does not store elements
  • Streams do not change the source object; instead, they return a new Stream that holds the result
  • Stream operations are delayed, which means they wait until they are needed

There are three steps to the operation of Stream

Create Stream

A data source (e.g. collection, array) to obtain a stream

Create a stream through the Stream() or parallelStream() provided by the Collection series Collection

@Test
public void createStream(){
    // 1: stream() or parallelStream() provided through Collection series Collection
    List list = new ArrayList<>();
    Stream stream = list.stream();
    Stream stringStream = list.parallelStream();
}

Get the array stream through the static method Stream() in Arrays

@Test
public void createStream(){
    // 2: Get the array stream through the static method Stream() in Arrays
    String[] strings = new String[10];
    Stream stream1 = Arrays.stream(strings);
}

Create a Stream through the static method of() in the Stream class

@Test
public void createStream(){
    // 3: Through the static method of() in the Stream class
    Stream stringStream1 = Stream.of("1", "2", "3");
}

Create wireless stream

Why is it called wireless flow? It should be because it is a flow returned by a statement that starts with execution but does not end. It can cycle all the time, so it is called wireless flow

iteration

@Test
public void createStream(){
    // 4: Create infinite flow
    // iteration
    Stream iterate = Stream.iterate(0, x -> x + 2);
    // Generate 10 values
    iterate.limit(10).forEach(System.out::println);
}

generate

@Test
public void createStream(){
    // 4: Create infinite flow
    // generate
    Stream generate = Stream.generate(Math::random);
    generate.forEach(System.out::println);
}

Intermediate operation

An intermediate operation chain that processes the data of the data source

create data source

Add two annotations to the Person entity class

@AllArgsConstructor
@NoArgsConstructor

create data source

List personList = Arrays.asList(
    new Person("Zhang San",18),
    new Person("Li Si",22),
    new Person("Wang Wu",27),
    new Person("Zhao Liu",31),
    new Person("Apocalypse",50)
);

Multiple intermediate operations can be linked to form a pipeline. Unless the termination operation is triggered on the pipeline, the intermediate operation will not perform any processing, but all processing at one time when the operation is terminated is called "lazy evaluation"

Screening and slicing

filter

Filter (predict P) - receives Lambda and excludes some elements from the stream

@Test
public void test2(){
    // Filter people older than 30
    personList.stream().filter(x -> x.getAge() > 30).forEach(System.out::println);
}

limit

limit(long maxSize) - truncates the stream so that its elements do not exceed the given number

@Test
public void test3(){
    // Get two people
    personList.stream().limit(2).forEach(System.out::println);
}

skip

skip(n) - skip elements and return a stream that discards the first N elements. If there are less than N elements in the stream, an empty stream is returned, which is complementary to limit(n)

@Test
public void test4(){
    // Get the last two digits older than 18
    personList.stream().filter(x -> x.getAge() > 18).skip(2).forEach(System.out::println);
}

distinct

distinct() - filter to remove duplicate elements through hashcode() and equals() of the elements generated by the stream

Modify data source add duplicate data

List personList = Arrays.asList(
    new Person("Zhang San",18),
    new Person("Zhang San",18),
    new Person("Zhang San",18),
    new Person("Li Si",22),
    new Person("Wang Wu",27),
    new Person("Zhao Liu",31),
    new Person("Apocalypse",50)
);
@Test
public void test5(){
    // De duplication / / if duplicates are not removed, Equals and HashCode can be rewritten
    personList.stream().distinct().forEach(System.out::println);
}

mapping

map

map - receive Lambda, convert elements into other forms or extract information, and receive a function as a parameter. The function will be applied to each element and mapped to a new element

@Test
public void test6(){
    List list = Arrays.asList("a", "b", "c", "d");
    // Convert all to uppercase
    list.stream().map(String::toUpperCase).forEach(System.out::println);
}

flatmap

flatmap - takes a function as an argument, replaces each value in the stream with another stream, and then links all streams into one stream

@Test
public void test7(){
    List list = Arrays.asList("aaa","bbb","ccc","ddd");
    //        Stream<Stream> streamStream = list.stream().map(TestStreamApi::stringToCharacter);
    // Expected to return a stream, so it will cause a nested stream structure
    //        streamStream.forEach(s -> {
    //            s.forEach(System.out::println);
    //        });
    // At this time, you can use flatmap to integrate all streams into one stream
    list.stream().flatMap(TestStreamApi::stringToCharacter).forEach(System.out::println);
}

sort

sorted - natural sort

sorted() - natural sort

@Test
public void test8(){
    List list = Arrays.asList("c", "f", "a", "d", "b");
    // Natural sorting
    list.stream().sorted().forEach(System.out::println);
}

sorted - custom sort

sorted(Comparator c) - custom sorting

@Test
public void test9(){
    personList.stream().sorted(((o1, o2) -> {
        if(o1.getAge().equals(o2.getAge())){
            return o1.getName().compareTo(o2.getName());
        }
        return o1.getAge().compareTo(o2.getAge());
    })).forEach(System.out::println);
}

Terminate operation (terminal operation)

A termination operation that executes an intermediate chain of operations and produces results

Find and match

allMatch - checks whether all elements are matched

anyMatch - checks if at least one element is matched

noneMatch - checks if all elements are not matched

findFirst - returns the first element

findAny - returns any element in the current stream

count - returns the total number of elements in the stream

max - returns the maximum value in the stream

min - returns the minimum value in the stream

@Test
public void test10() {
    // Are they all 18 years old
    boolean b = personList.stream().allMatch(e -> e.getAge().equals(18));
    System.out.println("is all match: " + b);
    // Is anyone 18
    boolean b1 = personList.stream().anyMatch(e -> e.getAge().equals(18));
    System.out.println("is any match: " + b1);
    // Is there anyone 18
    boolean b2 = personList.stream().noneMatch(e -> e.getAge().equals(18));
    System.out.println("is none match: " + b2);
    // Get the youngest person
    Optional first = personList.stream().sorted(Comparator.comparingInt(Person::getAge)).findFirst();
    System.out.println("first age is: " + first.get());
    // Look for anyone from a crowd of 18 years old
    Optional any = personList.stream().filter(s -> s.getAge().equals(18)).findAny();
    System.out.println("age 18 is: " + any.get());
    // Get total
    long count = personList.stream().count();
    System.out.println("count num is:" + count);
    // Get the oldest person
    Optional max = personList.stream().max(Comparator.comparingInt(Person::getAge));
    System.out.println("age max is: " + max);
    // Get the youngest person
    Optional min = personList.stream().min(Comparator.comparingInt(Person::getAge));
    System.out.println("age min is:" + min);

}

reduction

reduce(T identity,BinaryOperator) / reduce(BinaryOperator) - Elements in the flow can be combined repeatedly to obtain a value

@Test
public void test11(){
    // Calculate array sum
    List integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer reduce = integers.stream().reduce(0, Integer::sum);
    System.out.println(reduce);
    // Calculate the total age of personnel
    Optional reduce1 = personList.stream().map(Person::getAge).reduce(Integer::sum);
    System.out.println("person age count is : "+reduce1.get());
}

collect

collect - converts the Stream into other forms, receives the implementation of a Collector interface, and is used to summarize the elements in the Stream

@Test
public void test12(){

    // Collect the names of all personnel and convert them to List
    List collect = personList.stream().map(Person::getName).collect(Collectors.toList());
    System.out.println(" name list is : "+collect);
    // Collect the names of all personnel and convert them to Set
    Set collect1 = personList.stream().map(Person::getName).collect(Collectors.toSet());
    // Collect the names of all personnel and convert them to HashSet
    HashSet collect2 = personList.stream().map(Person::getName).collect(Collectors.toCollection(HashSet::new));

    // Get total
    Long collect3 = personList.stream().collect(Collectors.counting());
    System.out.println("counting is : "+collect3);

    // Get average age of personnel from
    Double collect4 = personList.stream().collect(Collectors.averagingInt(Person::getAge));
    System.out.println("avg is :" + collect4);

    // Get the total age of all personnel
    Integer collect5 = personList.stream().collect(Collectors.summingInt(Person::getAge));
    System.out.println("sum is : " + collect5);

    // Get the oldest
    Optional collect6 = personList.stream().collect(Collectors.maxBy((x, y) -> Integer.compare(x.getAge(), y.getAge())));
    System.out.println("max by is : "+collect6);

    // Get minimum age
    Optional collect7 = personList.stream().map(Person::getAge).collect(Collectors.minBy(Integer::compare));
    System.out.println("age min is:" + collect7);

    // Grouped by age
    Map<Integer, List> collect8 = personList.stream().collect(Collectors.groupingBy(Person::getAge));
    System.out.println("grouping by is:"+collect8);

    // Multilevel grouping
    Map<String, Map<String, List>> collect9 = personList.stream().collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getName)));
    System.out.println("Multilevel grouping:"+collect9);

    // Conditional partition
    Map<Boolean, List> collect10 = personList.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 25));
    System.out.println("partitioningBy is:"+collect10);

    // Multilevel partition
    Map<Boolean, Map<Boolean, List>> collect11 = personList.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 25, Collectors.partitioningBy(y -> y.getName().equals("Zhang San"))));
    System.out.println("Multilevel partition:"+collect11);

    // Gets the numeric calculation container class
    IntSummaryStatistics collect12 = personList.stream().collect(Collectors.summarizingInt(Person::getAge));
    System.out.println(collect12.getMax());
    System.out.println(collect12.getSum());
    System.out.println(collect12.getAverage());
    System.out.println(collect12.getMin());
    System.out.println(collect12.getCount());

    // Link owner's name by underline
    String collect13 = personList.stream().map(Person::getName).collect(Collectors.joining("-"));
    System.out.println("names is :" + collect13);
}
 name list is : [Zhang San, Zhang San, Zhang San, Li Si, Wang Wu, Zhao Liu, Apocalypse]
counting is : 7
avg is :26.285714285714285
sum is : 184
max by is : Optional[Person(name=Apocalypse, age=50)]
age min is:Optional[18]
grouping by is:{50=[Person(name=Apocalypse, age=50)], 18=[Person(name=Zhang San, age=18), Person(name=Zhang San, age=18), Person(name=Zhang San, age=18)], 22=[Person(name=Li Si, age=22)], 27=[Person(name=Wang Wu, age=27)], 31=[Person(name=Zhao Liu, age=31)]}
Multilevel grouping:{Li Si={Li Si=[Person(name=Li Si, age=22)]}, Zhang San={Zhang San=[Person(name=Zhang San, age=18), Person(name=Zhang San, age=18), Person(name=Zhang San, age=18)]}, Wang Wu={Wang Wu=[Person(name=Wang Wu, age=27)]}, Zhao Liu={Zhao Liu=[Person(name=Zhao Liu, age=31)]}, Apocalypse={Apocalypse=[Person(name=Apocalypse, age=50)]}}
partitioningBy is:{false=[Person(name=Zhang San, age=18), Person(name=Zhang San, age=18), Person(name=Zhang San, age=18), Person(name=Li Si, age=22)], true=[Person(name=Wang Wu, age=27), Person(name=Zhao Liu, age=31), Person(name=Apocalypse, age=50)]}
Multilevel partition:{false={false=[Person(name=Li Si, age=22)], true=[Person(name=Zhang San, age=18), Person(name=Zhang San, age=18), Person(name=Zhang San, age=18)]}, true={false=[Person(name=Wang Wu, age=27), Person(name=Zhao Liu, age=31), Person(name=Apocalypse, age=50)], true=[]}}
50
184
26.285714285714285
18
7
names is :Zhang San-Zhang San-Zhang San-Li Si-Wang Wu-Zhao Liu-Apocalypse

Parallel flow and sequential flow

Parallel flow is to divide a content into multiple data blocks and process each data block with different threads

Parallel is optimized in Java 8. We can easily operate data in parallel. The Stream API can declaratively switch between parallel flow and sequential flow through parallel() and sequential()

Sequential flow calculates the sum of 100 billion

@Test
public void test13(){
    Instant now = Instant.now();
    long reduce = LongStream.rangeClosed(0, 100000000000L)
        .reduce(0, Long::sum);
    System.out.println(reduce);
    Instant end = Instant.now();
    System.out.println("time consuming:"+Duration.between(now,end).toMillis());
}

CPU utilization

Execution time ms

932356074711512064
 time consuming:50036

The sum of 100 billion in parallel stream computing

@Test
public void test14(){
    Instant now = Instant.now();
    long reduce = LongStream.rangeClosed(0, 100000000000L)
        .parallel()
        .reduce(0, Long::sum);
    System.out.println(reduce);
    Instant end = Instant.now();
    System.out.println("time consuming:"+Duration.between(now,end).toMillis());
}

CPU utilization

Execution time ms

932356074711512064
 time consuming:20068

Switching method

sequential() switches to sequential flow

parallel() switches to parallel flow

Optional class

brief introduction

The optional class (java.util.Optional) is a container class that represents the existence or non existence of a value. Originally, null was used to indicate that a value does not exist. Now optional can better express this concept and avoid null pointer exceptions

common method

of

Optional. Of (T) - create an instance of optional

@Test
public void test1(){
    // Create a person
    Optional person = Optional.of(new Person());
    // Get the value in
    Person person1 = person.get();
    System.out.println("get method : " + person1);
}
get method : Person(name=null, age=null)

empty

Optional.empty() - create an empty optional instance

@Test
public void test2(){
    Optional<Object> empty = Optional.empty();
    System.out.println(empty);
}

ofNullable

Optional. Ofnullable (T) - create an optional instance if t is not null, otherwise create an empty instance

@Test
public void test3(){
    Optional<Object> empty = Optional.ofNullable(null);
    System.out.println(empty);
    Optional<Object> empty1 = Optional.ofNullable(new Person());
    System.out.println(empty1);
}

isPresent

isPresent() - judge whether to include a value

@Test
public void test4(){
    Optional<Object> empty = Optional.ofNullable(null);
    System.out.println(empty.isPresent());
    Optional<Object> empty1 = Optional.ofNullable(new Person());
    System.out.println(empty1.isPresent());
}

orElse

Orelse (T) - returns the value if the calling object contains a value, otherwise returns t

@Test
public void test5(){
    Optional<Object> empty = Optional.ofNullable(null);
    System.out.println(empty.orElse("this is null"));
    Optional<Object> empty1 = Optional.ofNullable(new Person());
    System.out.println(empty1.orElse("this is null"));
}

orElseGet

orElseGet(Supplier s) - returns the value if the calling object contains a value, otherwise returns the value obtained by S

@Test
public void test6(){
    Optional<Object> empty = Optional.ofNullable(null);
    System.out.println(empty.orElseGet(Person::new));
}

map

map(Function f) - if there is a value, process it and return the processed Optional. Otherwise, return Optional empty

@Test
public void test7(){
    Optional empty = Optional.of(new Person("lisa", 18));
    Optional s = empty.map(Person::getName);
    System.out.println(s.get());
}
lisa

flatMap

flatMap(Function mapper) - similar to Map, the return value must be Optional

@Test
public void test8(){
    Optional empty = Optional.of(new Person("lisa", 18));
    Optional s = empty.flatMap(e -> Optional.of(e.getName()));
    System.out.println(s.get());
}