Basic knowledge of java -- Stream API for streaming computing

Posted by LarryK on Fri, 04 Feb 2022 08:26:30 +0100

1, Basic concepts

In order to make it easier for engineers to process aggregate data, a new feature in Java 8 is Stream (also known as Stream computing). Using the newly provided Stream API, Java can process collections declaratively and iteratively. It is also similar to the real pipeline production: flow = operation + node + data.

Precautions for flow:

  • Lazy evaluation: if there is no termination operation, the intermediate operation will not be executed
  • Flow is one-time: once a flow object passes through a termination operation, the flow can no longer be used
  • It will not affect the original data: we can do a lot of processing on the data in the stream, but it will not affect the elements in the original set under normal circumstances

In order to more intuitively compare the methods of traditional set or array operation and flow calculation, the following preparations are made:

Define the Author class:

package StreamAPI;

import java.util.List;
@EqualsAndHashCode
public class Author {
    private long id;

    private String name;

    private Integer age;

    private String intro;

    private List<Book> books;

    public Author(long id, String name, Integer age, String intro, List<Book> books) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.intro = intro;
        this.books = books;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getIntro() {
        return intro;
    }

    public void setIntro(String intro) {
        this.intro = intro;
    }

    public List<Book> getBooks() {
        return books;
    }

    public void setBooks(List<Book> books) {
        this.books = books;
    }
}

   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Author author = (Author) o;
        return id == author.id && Objects.equals(name, author.name) && Objects.equals(age, author.age) && Objects.equals(intro, author.intro) && Objects.equals(books, author.books);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, intro, books);
    }

Define the Book class:

package StreamAPI;
@EqualsAndHashCode
public class Book {
    private long id;

    private String name;

    private String category;

    private Integer score;

    private String intro;

    public Book(long id, String name, String category, Integer score, String intro) {
        this.id = id;
        this.name = name;
        this.category = category;
        this.score = score;
        this.intro = intro;
    }
   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return id == book.id && Objects.equals(name, book.name) && Objects.equals(category, book.category) && Objects.equals(score, book.score) && Objects.equals(intro, book.intro);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, category, score, intro);
    }
}

Test class:

package StreamAPI;

import com.company.Task4_3;

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

public class StreamDemo {
    public static void main(String[] args) {

    }

    private static List<Author> getAuthors(){
        //Data initialization
        Author author = new Author(1L,"Mondo",33,"A Zuan who understands philosophy from a kitchen knife",null);
        Author author2 = new Author(2L,"Arazo",15,"The wind couldn't catch up with his speed of thinking",null);
        Author author3 = new Author(3L,"easy",14,"It's the world that limits his thinking",null);
        Author author4 = new Author(3L,"easy",14,"It's the world that limits his thinking",null);

        //List of books
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L,"On both sides of the knife are light and darkness","Philosophy, love",88,"Divide love and hate with a knife"));
        books1.add(new Book(2L,"One cannot die by the same knife","Personal growth, love",99,"Tell about the loss of evil"));

        books2.add(new Book(3L,"Where the wind cannot reach","Philosophy",85,"Philosophical thought"));
        books2.add(new Book(3L,"Where the wind cannot reach","Philosophy",85,"Philosophical thought"));
        books2.add(new Book(3L,"Where the wind cannot reach","Philosophy",85,"Philosophical thought"));
        books2.add(new Book(4L,"Blowing or not blowing","Love, biography",56,"Philosophy"));

        books3.add(new Book(5L,"Your sword is my sword","love",56,"The sword"));
        books3.add(new Book(6L,"Wind and sword","Personal biography",100,"Two philosophers"));
        books3.add(new Book(6L,"Wind and sword","Personal biography",100,"Two philosophers"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));
        return authorList;
    }
}

Now, to get the names of writers younger than 18 years old, we can print them in the following ways by using stream API:

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()//Convert a collection into a stream
                .distinct()//Remove duplicate elements
                .filter(author->author.getAge()<18) //Filter eligible authors
                .forEach(author->System.out.println(author.getName()));
    }

2, Create stream

Single column collection: collection object stream()

	List<Author> authors = getAuthors();
	Stream<Author> stream = authors.stream();

Array: arrays Stream (array) or stream Of to create

	Integer[] arr = {1,2,3,4,5};
	Stream<Integer> stream = Arrays.stream(arr);
	Stream<Integer> stream2 = Stream.of(arr);

Double column set: convert to single column set and then create

	Map<String,Integer> map = new HashMap<>();
	map.put("Crayon Shin Chan",19);
	map.put("sunspot",17);
	map.put("Xiang Yang ri",16);
	Stream<Map.Entry<String,Integer>> stream = map.entrySet().stream();

3, Intermediate operation

3.1 filter

You can conditionally filter the elements in the stream, and only those that meet the filtering conditions can continue to stay in the stream.

For example: output the name of a writer whose name length exceeds 1

        List<Author> authors = getAuthors();
        authors.stream()//Convert a collection into a stream
                .filter(author->author.getName().length()>1)
                .forEach(author->System.out.println(author.getName()));

3.2 map

You can calculate or convert elements in a stream

For example, print the names of all writers, or convert the collection to string type for later operation

 List<Author> authors = getAuthors();
        authors.stream()
                .map(new Function<Author, String>() {
                    @Override
                    public String apply(Author author) {
                        return author.getName();
                    }
                })
                .forEach(s->System.out.println(s));

3.3 distinct

The duplicate elements in the stream can be removed. It depends on the equals method of Object to judge whether it is the same Object. Note that you need to override the equals method

If you print the names of all writers, there must be no duplicate elements

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .forEach(author->System.out.println(author.getName()));

3.4 sorted

The elements in the stream can be sorted, and the Comparable interface should be implemented

For example, the elements in convection are arranged in descending order according to age, and there shall be no duplicate elements.
The first method is to call the nonparametric sorted() method. To implement the Comparable interface, modify the code of the Author class first:

package StreamAPI;

import lombok.EqualsAndHashCode;

import java.util.List;
import java.util.Objects;

@EqualsAndHashCode
public class Author implements Comparable<Author> {
    private long id;

    private String name;

    private Integer age;

    private String intro;

    private List<Book> books;

    @Override
    public int compareTo(Author o) {
        return o.getAge() - this.getAge();
    }
}

Then arrange the elements in descending order of age:

    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted()
                .forEach(author->System.out.println(author.getName()));
    }

The second method: directly call the sorted() method with parameters without implementing the Comparable interface:

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1,o2)->o2.getAge() - o1.getAge())
                .forEach(author->System.out.println(author.getName()));

3.5 limit

The maximum length of the stream can be set, and the excess will be discarded

For example, the elements in the convection are arranged in descending order of age, and it is required that there should be no duplicate elements, and then print the two oldest elements

 List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1,o2)->o2.getAge() - o1.getAge())
                .limit(2)
                .forEach(author->System.out.println(author.getName()));

3.6 skip

Skip the n elements in the stream and return the remaining elements

If other writers other than the oldest ones are printed out, it is required that there should be no duplicate elements and they should be arranged in descending order of age

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1,o2)->o2.getAge() - o1.getAge())
                .skip(1)//Skip first
                .forEach(author->System.out.println(author.getName()));

3.7 flatMap

map can only convert one object into another object as an element in the stream. flatMap can convert an object into multiple objects as elements in the stream

For example, when printing the names of all books, it is required to remove duplicate elements

 List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(new Function<Author, Stream<Book>>() {
                    @Override
                    public Stream<Book> apply(Author author) {
                        return author.getBooks().stream();
                    }
                })
                .distinct()
                .forEach(book->System.out.println(book.getName()));

For example, printing all classifications of existing data requires de duplication of the classification. This format cannot appear: philosophy, love

        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author->author.getBooks().stream())
                .distinct()
                .flatMap(book->Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(category->System.out.println(category));

4, Terminate operation

4.1 forEach

The elements in the stream are traversed. We specify the specific operation of the traversed elements through the passed parameters

4.2 count

It can be used to obtain the number of elements in the current stream

For example, print the number of all books of these writers, and pay attention to removing duplicate elements

        List<Author> authors = getAuthors();
        long count = authors.stream()
                    .flatMap(author->author.getBooks().stream())
                    .distinct()
                    .count();
        System.out.println(count);

4.3 max&min

You can get the latest value in the stream

For example: get the highest and lowest scores of these writers' books
Stream < author > -- > stream < book > -- > stream < integer > (the type of score is int)

 List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author->author.getBooks().stream())
                .map(book->book.getScore())
                .max((score1,score2)->score1-score2);

        Optional<Integer> min = authors.stream()
                .flatMap(author->author.getBooks().stream())
                .map(book->book.getScore())
                .min((score1,score2)->score1-score2);

        System.out.println(min.get());
        System.out.println(max.get());

4.4 collect

Converts the current stream into a collection

For example: get a List collection that holds all the author names

        List<Author> authors = getAuthors();
       List<String> nameList = authors.stream()
               .map(author->author.getName())
               .collect(Collectors.toList());
       System.out.println(nameList);

For example: get a Set of all book titles

       Set<Book> books = authors.stream()
               .flatMap(author->author.getBooks().stream())
               .collect(Collectors.toSet());
       System.out.println(books);

For example: get a map set. The key of the map is the author's name and the value is list < book >

       Map<String,List<Book>> map = authors.stream()
       			.distinct()
               .collect(Collectors.toMap(author->author.getName(),author->author.getBooks()));

4.4 find and match

anyMatch: used to judge whether there are any matching elements. The result is boolean

For example, judge whether there are writers over the age of 29

List<Author> authors = getAuthors();
       boolean flag = authors.stream()
               .anyMatch(author->author.getAge()>29);
       System.out.println(flag);

allMatch: used to judge whether all matching conditions are met

noneMatch: judge whether the matching conditions are not met

findAny: get any element in the stream. This method can't guarantee which element in the stream is obtained

findFirst: gets the first element in the stream

4.5 reduce

By combining the elements in the stream, we can pass in an initial value, which will take the elements in the stream at one time according to our calculation method and calculate on the basis of the initial value, and then calculate the calculation result with the following elements.

Its internal calculation method is as follows:

	T result = identity;
	for(T element : this stream)
		result = accumulator.apply(result,element)
	return result;

Among them, identity is the initial value that we can pass in through the method parameters, and the specific calculation of calculator apply is also determined through the method parameters

For example, sum the ages of all authors

        List<Author> authors = getAuthors();
        Integer sum = authors.stream()
                .distinct()
                .map(author->author.getAge())
                .reduce(0,(result,element)->result+element);//The initial value is 0, the result is the final variable, and the element is the element value traversed
        System.out.println(sum);

practice

Existing Employee entity class:

package StreamAPI;

public class Employee {
    public enum Type {MANAGER,SELLER,OFFICER};

    private String name;
    private String genger;
    private Integer age;
    private boolean married;
    private Type type;

    public Employee(String name, String genger, Integer age, boolean married, Type type) {
        this.name = name;
        this.genger = genger;
        this.age = age;
        this.married = married;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGenger() {
        return genger;
    }

    public void setGenger(String genger) {
        this.genger = genger;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public boolean isMarried() {
        return married;
    }

    public void setMarried(boolean married) {
        this.married = married;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", genger='" + genger + '\'' +
                ", age=" + age +
                ", married=" + married +
                ", type=" + type +
                '}';
    }
}

Company entity class:

package StreamAPI;

import java.util.List;

public class Company<T> {
    public enum Type{BIG,SMALL};

    private String name;
    private Type type;
    private List<T> employee;

    public Company(String name, Type type, List<T> employee) {
        this.name = name;
        this.type = type;
        this.employee = employee;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public List<T> getEmployee() {
        return employee;
    }

    public void setEmployee(List<T> employee) {
        this.employee = employee;
    }

    @Override
    public String toString() {
        return "Company{" +
                "name='" + name + '\'' +
                ", type=" + type +
                ", employee=" + employee +
                '}';
    }
}

Insert the following data into the set:

 List<Company<Employee>> list = new ArrayList<>();
        List<Employee> employees = Arrays.asList(
                new Employee("Yong Zhang","male",28,true,Employee.Type.MANAGER),
                new Employee("Li Qiang","male",22,false,Employee.Type.SELLER),
                new Employee("Wang Wu","male",32,false,Employee.Type.SELLER),
                new Employee("Mei Li","female",26,true,Employee.Type.OFFICER),
                new Employee("Zheng Shuai","male",29,false,Employee.Type.OFFICER),
                new Employee("Zeng Mei","female",27,true,Employee.Type.SELLER),
                new Employee("Hao Jun","male",22,true,Employee.Type.SELLER),
                new Employee("Square","female",24,false,Employee.Type.SELLER)
                );
        Company<Employee> moubao = new Company<Employee>("A treasure",Company.Type.BIG,employees);

        employees = Arrays.asList(
                new Employee("Wu Qiong","female",27,true,Employee.Type.SELLER),
                new Employee("Chen Chen","female",28,false,Employee.Type.OFFICER),
                new Employee("Lennon ","male",35,true,Employee.Type.OFFICER),
                new Employee("Zhou Qi","male",29,false,Employee.Type.OFFICER),
                new Employee("Wang Wang","male",21,false,Employee.Type.OFFICER),
                new Employee("Muddle through","male",27,false,Employee.Type.OFFICER),
                new Employee("Mao Yang","male",34,true,Employee.Type.MANAGER),
                new Employee("Jian Zhu","male",30,false,Employee.Type.MANAGER)
        );
        Company<Employee> mouxin = new Company<Employee>("Some East",Company.Type.BIG,employees);

        employees = Arrays.asList(
                new Employee("Feng Guo","male",35,false,Employee.Type.SELLER),
                new Employee("He Hua","female",27,false,Employee.Type.MANAGER),
                new Employee("Wei Jing","male",25,true,Employee.Type.OFFICER),
                new Employee("construction","male",28,false,Employee.Type.OFFICER),
                new Employee("Shen Yue","female",24,false,Employee.Type.OFFICER),
                new Employee("Le Huan","female",22,false,Employee.Type.OFFICER),
                new Employee("security","male",33,true,Employee.Type.MANAGER),
                new Employee("Linsen","male",26,true,Employee.Type.SELLER)
        );
        Company<Employee> wahaha = new Company<Employee>("Some ha ha",Company.Type.SMALL,employees);

        //Add to list
        list.add(moubao);
        list.add(mouxin);
        list.add(wahaha);

Realize the following requirements: count all unmarried employees whose company type is BIG, sort them by age, and unmarried employees do not distinguish companies, that is, the final result is put in a list (hint: the whole requirement can be completed with one line of code)

        List<Employee> employeeList = list.stream()
                .filter(conpany->conpany.getType() == Company.Type.BIG)
                .flatMap(company->company.getEmployee().stream())
                .filter(employee -> employee.isMarried() == false)
                .sorted((o1,o2)->o1.getAge() - o2.getAge() )
                .collect(Collectors.toList());
        for(Employee e : employeeList)
            System.out.println(e.toString());

Topics: Java Back-end stream