Simple and Convenient Collection Processing-Java 8Stream Stream Stream

  gather, java, stream

Background

Java 8 has been released for several years and java 12 has been released some time ago, but in normal work, the environment of many projects still stays in java1.7. Moreover, many new features of java8 are revolutionary, such as optimization of various sets, lambda expressions, etc., so we still need to understand the charm of java8.

Today we are going to learn the Stream of java8. We don’t need any theoretical basis, we can use it directly.

The reason why I contacted stream is that I want to do a data analysis of users’ income and consumption. At first, the statistical screening groups were all intended to show the results directly from Mysql in SQL language. However, during the operation, we found that such frequent access to the database would greatly affect the performance and the analysis speed would be very slow. Therefore, we hope that we can get all the data by accessing the database once, and then put it into memory for data analysis, statistics and filtering.

Next, I looked at stream’s API and found that this was what I wanted.

I. Stream understanding

In java we call Stream”Flow“We often use streams to perform some pipelining operations on collections. Stream is like a factory. It just needs to instill sets, commands and some parameters into theAssembly lineGo to, can be processed into the desired results. This pipeline can greatly simplify code and reduce operation.

Second, Stream process

原集合 —> 流  —> 各种操作(过滤、分组、统计) —> 终端操作

The operation flow of Stream flow is generally like this. First, the set is converted into a flow, and then it goes through various operations, such as filtering, filtering, grouping and calculation. The final terminal operation is to convert it into the data we want. This data is usually in the form of a set, and sometimes it will output a count count according to requirements. Examples will be given below.

在这里插入图片描述

III. Examples of API Functions

First, define a user object, including four member variables of name, age, gender and native place:

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Log4j
@Builder
public class User {
    //姓名
    private String name;
    //年龄
    private Integer age;
    //性别
    private Integer sex;
    //所在省市
    private String address;
}

Lombok is used here to simplify the code of entity classes.

Then create the requiredAggregate data, that is, the source data:

//1.构建我们的list
List<User> list= Arrays.asList(
        new User("钢铁侠",40,0,"华盛顿"),
        new User("蜘蛛侠",20,0,"华盛顿"),
        new User("赵丽颖",30,1,"湖北武汉市"),
        new User("詹姆斯",35,0,"洛杉矶"),
        new User("李世民",60,0,"山西省太原市"),
        new User("蔡徐坤",20,1,"陕西西安市"),
        new User("葫芦娃的爷爷",70,0,"山西省太原市")
);

3.1 filtration

1) create streamstream ()/parallelstream ()

  • Stream (): serial stream
  • ParallelStream (): parallel stream

2)filter filtering (T-> boolean)

For example, to filter users over 40 years old, you can write like this:

List<User> filterList = list.stream().filter(user -> user.getAge() >= 40)
        .collect(toList());

Inside the filter,-> the arrow is followed by aBoolean value, you can write any filter condition, which is equivalent to what follows where in sql, in other words,Functions that can be realized by sql can be realized here.

Print results:

在这里插入图片描述

3)distinct

It is very similar to the distinct keyword in sql. In order to see the effect, we have added a duplicate to the original collection. Let’s choose Iron Man. The 4 Iron Man of Fu Lian was killed unfortunately. Everyone is still sad.

List<User> list= Arrays.asList(
        new User("钢铁侠",40,0,"华盛顿"),
        new User("钢铁侠",40,0,"华盛顿"),
        new User("蜘蛛侠",20,0,"华盛顿"),
        new User("赵丽颖",30,1,"湖北武汉市"),
        new User("詹姆斯",35,0,"洛杉矶"),
        new User("李世民",60,0,"山西省太原市"),
        new User("蔡徐坤”,18,1,"陕西西安市"),
        new User("葫芦娃的爷爷",70,0,"山西省太原市")
);
//distinct 去重
List<User> distinctList = filterList.stream().distinct()
        .collect(toList());

Print results:

在这里插入图片描述

4)sorted sorting

If the class of the element in the stream implements the Comparable interface, that is, it has its own sorting rules, then it can be called directlySorted () methodSort elements, such as:

Comparator.comparingInt

On the contrary, you need to call sorted((T, T) -> int) to implement the Comparator interface.

//sorted()
List<User> sortedList = distinctList.stream().sorted(Comparator.comparingInt(User::getAge))
        .collect(toList());

Print results:

在这里插入图片描述

The results were sorted according to age.

5)limit () returns the first n elements

If you want to know who is the youngest, you can do the following:

//limit 返回前n个元素
List<User> limitList = sortedList.stream().limit(1)
        .collect(toList());

在这里插入图片描述

6)skip()

Contrary to limit, skip means to skip, that is, to remove the first n elements.

Print results:

在这里插入图片描述

Sure enough, the first two were removed, leaving only the oldest grandpa Huludao.

3.2 mapping

1)map(T->R)

Map is to convert data of type T into data of type R, for example, we want to set up a new list to store all the city information of users.

//map(T->R)
List<String> cityList = list.stream().map(User::getAddress).distinct().collect(toList());

Print results:

在这里插入图片描述

2)flatMap(T -> Stream<R>)

Each element T in the stream is mapped into a stream, and then each stream is connected into a stream.

//flatMap(T -> Stream<R>)
List<String> flatList = new ArrayList<>();
flatList.add("唱,跳");
flatList.add("rape,篮球,music");
flatList = flatList.stream().map(s -> s.split(",")).flatMap(Arrays::stream).collect(toList());

Print results:

在这里插入图片描述

Here, the data in the original set are separated by commas. after splitting with split, the Stream<String[] >, which is composed of string arrays, is obtained. the flatMap is used

Arrays::stream

Turn Stream<String[] > into Stream<String >, and then connect the streams to form a complete singing, dancing, rap, basketball and music.

3.3 search

1)allMatch(T->boolean)

It is necessary to check whether all the parameters are satisfied. If these users are the list of Internet users in Internet cafes, then it is necessary to check whether everyone has reached the age of 18.

boolean isAdult = list.stream().allMatch(user -> user.getAge() >= 18);

Print results:

true

2)anyMatch(T->boolean)

Check whether any element meets the given conditions, for example, to know whether there are girls on the classmate list.

//anyMatch(T -> boolean) 是否有任意一个元素满足给定的条件
boolean isGirl = list.stream().anyMatch(user -> user.getSex() == 1);

Print results:

true

This indicates that there are girls in the collection.

3)noneMatch(T -> boolean)

Whether there are elements in the stream that match the given T -> boolean condition.

For example, check whether there are any users from Paris.

boolean isLSJ = list.stream().noneMatch(user -> user.getAddress().contains("巴黎"));

Print results:

true

Print true to indicate that there are no users in Paris.

4)findFirst (): Find the first element

Optional<User> fristUser  = list.stream().findFirst();

Print results:

User(name=钢铁侠, age=40, sex=0, address=华盛顿)

5)findAny (): find any element

Optional<User> anyUser  = list.stream().findAny();

Print results:

User(name=钢铁侠, age=40, sex=0, address=华盛顿)

Here we find that findAny always returns the first element, so why make a distinction? Because in parallel flowparallelStream()It is indeed any element found in.

Optional<User> anyParallelUser  = list.parallelStream().findAny();

Print results:

Optional[User(name=李世民, age=60, sex=0, address=山西省太原市)]

3.4 inductive calculation

1) Find the total number of users

long count = list.stream().collect(Collectors.counting());

We can abbreviate it to:

long count = list.stream().count();

Running results:

8

2) obtaining the maximum and minimum values of a certain attribute

// 求最大年龄
Optional<User> max = list.stream().collect(Collectors.maxBy(
Comparator.comparing(User::getAge)));

// 求最小年龄
Optional<User> min = list.stream().collect(Collectors.minBy(
Comparator.comparing(User::getAge)));

Running results:

在这里插入图片描述

在这里插入图片描述

3) What is the sum of ages

// 求年龄总和
int totalAge = list.stream().collect(Collectors.summingInt(User::getAge));

Running results:

313

We often use BigDecimal to record money, assuming we want the sum of BigDecimal:

// 获得列表对象金额, 使用reduce聚合函数,实现累加器
BigDecimal sum = myList.stream() .map(User::getMoney)
.reduce(BigDecimal.ZERO,BigDecimal::add);

4) Calculate the average age

//求年龄平均值
double avgAge = list.stream().collect(
Collectors.averagingInt(User::getAge));

Running results:

39.125

5) obtaining the number, sum, maximum value and minimum value of elements at one time

IntSummaryStatistics statistics = list.stream().collect(
Collectors.summarizingInt(User::getAge));

Running results:

在这里插入图片描述

6) String splicing

To connect users’ names into a string and separate them with commas.

String names = list.stream().map(User::getName)
.collect(Collectors.joining(", "));

Running results:

钢铁侠, 钢铁侠, 蜘蛛侠, 赵丽颖, 詹姆斯, 李世民, 蔡徐坤, 葫芦娃的爷爷

3.5 Grouping

In database operations, we often group the queried data through the GROUP BY keyword. java8 streaming also provides grouping function. UseCollectors.groupingByTo group.

1) Users can be grouped according to their cities

Map<String, List<User>> cityMap = list.stream()
.collect(Collectors.groupingBy(User::getAddress));

在这里插入图片描述

The result is a map, key is a non-repeating city name, and value is a list of users belonging to the city. Grouping has been implemented.

2) Secondary grouping, first according to city grouping and then according to gender grouping

Map<String, Map<Integer, List<User>>> group = list.stream().collect(
        Collectors.groupingBy(User::getAddress, // 一级分组,按所在地区
                Collectors.groupingBy(User::getSex))); // 二级分组,按性别

Running results:

在这里插入图片描述

3) if you just want to count the number of users in each city, you don’t need a corresponding list

Group by city and count number:

Map<String, Long> cityCountMap = list.stream()
.collect(Collectors.groupingBy(User::getAddress,Collectors.counting()));

Running results:

在这里插入图片描述

4) Of course, it is also possible to filter before grouping and counting the number of people.

Map<String,Long> map = list.stream().filter(user -> user.getAge() <= 30)
        .collect(Collectors.groupingBy(User::getAddress,Collectors.counting()));

Running results:

在这里插入图片描述

5)partitioningBy partition

The difference between partitioning and grouping is that partitioning is based ontrueAndfalseTherefore, lambda of the parameter that partitioningBy accepts is alsoT -> boolean

//根据年龄是否小于等于30来分区
Map<Boolean, List<User>> part = list.stream()
        .collect(partitioningBy(user -> user.getAge() <= 30));

Running results:

在这里插入图片描述

Summary

So far, we have used a lot of stream functions, feeling a bit dazed but omnipotent. stream can do much more than that.

We can learn to use stream more and reconstruct the original complex sql query and the complex code for loop over and over again to make the code moreSimple and easy to understand, readable.

Expand reading:Redis Special Topic (1): Building Knowledge Map

Redis Topic (2): Exploring the Bottom of Redis Data Structure

By Yang Heng

Source: Yixin Institute of Technology