What you need to know about the use of Java quick development stream (1) Stream performs query, statistics and other operations on the List collection

 Introduction to Stream

The Java 8 API adds a new abstraction called Stream that lets you manipulate data in a declarative way. Stream provides a high-level abstraction for Java collection operations and representations in an intuitive way similar to querying data from a database with SQL statements.
Stream API can greatly improve the productivity of Java programmers, allowing programmers to write efficient, clean and concise code.
This style regards the collection of elements to be processed as a stream, which is transmitted in the pipeline and can be processed on the nodes of the pipeline, such as filtering, sorting, aggregation, etc.
The element flow is processed by the intermediate operation in the pipeline, and finally the result of the previous processing is obtained by the terminal operation.


 What is Stream?

Stream (stream) is a queue of elements from a data source and supports aggregation operations

  • Elements are objects of a specific type that form a queue. Stream in Java does not store elements, but calculates them on demand.
  • The source of the data  source stream . It can be a collection, an array, an I/O channel, a generator, etc.
  • Aggregation operations  are similar to SQL statements, such as filter, map, reduce, find, match, sorted, etc.

Unlike previous Collection operations, Stream operations also have two basic features:

  • Pipelining : Intermediate operations will return the stream object itself. In this way, multiple operations can be concatenated into a pipeline, just like the fluent style. Doing so allows for optimizations such as laziness and short-circuiting.
  • Internal iteration : In the past, collection traversal was done through Iterator or For-Each, and iterated outside the collection explicitly, which is called external iteration. Stream provides an internal iteration method, implemented through the visitor pattern (Visitor).

Why do you need Stream 

As a highlight of Java 8, Stream is a completely different concept from InputStream and OutputStream in the java.io package. It is also different from StAX's Stream for XML parsing, nor is it the Stream for Amazon Kinesis' real-time processing of big data. Stream in Java 8 is an enhancement to the collection (Collection) object function, which focuses on various very convenient and efficient aggregation operations (aggregate operation) or bulk data operations (bulk data operation) on collection objects. The Stream API greatly improves programming efficiency and program readability by means of the same emerging Lambda expression. At the same time, it provides serial and parallel modes for aggregation operations. The concurrent mode can make full use of the advantages of multi-core processors, and use fork/join parallel mode to split tasks and speed up the processing process. It is usually difficult and error-prone to write parallel code, but using the Stream API can easily write high-performance concurrent programs without writing a single line of multi-threaded code. Therefore, java.util.stream, which first appeared in Java 8, is a product of the comprehensive influence of a functional language + multi-core era.

What is an aggregation operation

In traditional J2EE applications, Java code often has to rely on relational database aggregation operations to accomplish things such as:

  • Average customer spending per month
  • Most Expensive Items for Sale
  • Valid orders completed this week (excluding invalid ones)
  • Take ten data samples as homepage recommendations

Such operations.

However, in today's era of data explosion, when data sources are diversified and data is massive, it is often necessary to leave the RDBMS, or perform higher-level data statistics based on the data returned from the bottom layer. In Java's collection API, there are only a small number of auxiliary methods, and more often, programmers need to use Iterator to traverse the collection and complete related aggregation application logic. This is a far from efficient, clumsy approach. In Java 7, if you want to find all transactions whose type is grocery, then return a set of transaction IDs sorted in descending order of transaction values

 generate flow

In Java 8, the collection interface has two methods to generate streams:

  • stream()  − Creates a serial stream for a collection.

  • parallelStream()  − Creates a parallel stream for a collection.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

Methods in the Collector interface:

1 List toList() Collect elements in the stream to List
Example: List<Employee> emps= list.stream().collect(Collectors.toList());

2 Set toSet() Collect elements in the stream to Set
Example: Set<Employee> emps= list.stream().collect(Collectors.toSet());

3 Collection toCollection() Collect the elements in the stream to the created collection
Example: Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));

4 Long counting() Count the number of elements in the stream
Example: long count = list.stream().collect(Collectors.counting());

5 Integer summingInt() Sums the integer attributes of the elements in the stream
Example: int total=list.stream().collect(Collectors.summingInt(Employee::getAge));

6 Double averagingInt() Calculate the average value of the Integer attribute of elements in the stream
Example: double avg = list.stream().collect(Collectors.averagingInt(Employee::getAge));

7 IntSummaryStatistics summarizingInt() Collects the statistical values ​​of Integer attributes in the stream. Such as: average value
Example: int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getAge));

String joining() String joins each string in the stream
Example: String str= list.stream().map(Employee::getName).collect(Collectors.joining());
Example: String str= list.stream() .map(Employee::getName).collect(Collectors.joining(",", "{", "}")));
Example: String str= list.stream().map(Employee::getName).collect (Collectors.joining(","));
9: Optional maxBy() Select the maximum value according to the comparator
Example: Optional<Emp>max= list.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Employee:: getSalary)));

10:Optional minBy() Select the minimum value according to the comparator
Example: Optional<Emp> min = list.stream().collect(Collectors.minBy(Comparator.comparingDouble(Employee::getSalary)));

11:reducing() starts from an initial value as an accumulator, and uses BinaryOperator to combine elements in the stream one by one to reduce to a single value
Example: int total=list.stream().collect(Collectors.reducing(0, Employee ::getAge, Integer::sum));

12: The type returned by the collectingAndThen() conversion function wraps another collector.
Example of the result conversion function: int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)) ;

13:Map<K, List> groupingBy() Group streams according to a certain attribute value, the attribute is K, and the result is V
Example: Map<String, List<Employee>> map= list.stream().collect(Collectors.groupingBy( Employee::getName));

14:Map<Boolean, List> partitioningBy() Partition according to true or false after collection
Example: employees.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary()>8000, Collectors.mapping(Employee: :getName,Collectors.counting()))).forEach((Key, value)-> System.out.println(Key+"----"+value));

15:The mapping() method will apply the result to another collector.
Example: employees.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary()>8000, Collectors.mapping(Employee::getName,Collectors.toList()))).forEach((Key, value) -> System.out.println(Key+"----"+value));

16: flatMapping () – similar to the Collectors.mapping () method, but finer granularity. Both take a function and a collector parameter to collect elements, but the flatMapping function receives the element stream, and then performs accumulation operations through the collector, which is generally used to process nested streams in collect()

17:filtering()–similar to the Stream filter() method, used to filter input elements, often used in conjunction with groupingBy/partitioningBy

18:toUnmodifiableMap() – Gather elements into an unmodifiable Map. The object addresses in the Map cannot be modified. If the objects in it support modification, they can actually be modified.

19:toUnmodifiableSet()—gather elements into an unmodifiable HashSet

20:toMap()—gather elements into a map

21:toConcurrentMap()—gather elements into a concurrentHashMap that supports concurrency

22:toUnmodifiableList()—gather elements into an unmodifiable ArrayList

23: groupingByConcurrent()—similar to groupingBy(), but its aggregated collection is concurrentHashMap supports concurrency, which can improve the efficiency of parallel stream grouping

24:teeing() – Returns a collector consisting of two downstream collectors. Each element passed to the resulting collector is processed by downstream collectors, whose results are then merged into the final result using the specified merge function.
Supports collecting streams using two separate collectors and then merging the results using the provided bifunction.
 

 Comprehensive example

1. Create entity classes and data

User.java

import java.math.BigDecimal;
 
/**
 * 用户信息实体类 by 青冘
 **/
public class User
{
    private int id; //用户ID
    private String name; //用户名称
    private String sex; //性别
    private int age; //年龄
    private String department; //部门
    private BigDecimal salary; //薪资
 
    //构造方法
    public User(int id,String name,String sex,int age,String department,BigDecimal salary)
    {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.department = department;
        this.salary = salary;
    }
    public int getId()
    {
        return id;
    }
    public void setId(int id)
    {
        this.id = id;
    }
    public String getName()
    {
        return name;
    } 
    public void setName(String name)
    {
        this.name = name;
    } 
    public String getSex()
    {
        return sex;
    } 
    public void setSex(String sex)
    {
        this.sex = sex;
    } 
    public int getAge()
    {
        return age;
    } 
    public void setAge(int age)
    {
        this.age = age;
    } 
    public String getDepartment()
    {
        return department;
    } 
    public void setDepartment(String department)
    {
        this.department = department;
    } 
    public BigDecimal getSalary()
    {
        return salary;
    } 
    public void setSalary(BigDecimal salary)
    {
        this.salary = salary;
    } 
    @Override
    public String toString()
    {
        return "ID:" + this.id + " 名称:" + this.name +  " 性别:" + this.sex
                + " 年龄:" + this.age + " 部门:" + this.department + " 薪资:" + this.salary + "元";
    }
}

UserService.class

import com.pjb.streamdemo.entity.User;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 用户信息业务逻辑类 by 青冘
 **/
public class UserService
{
    /**
     * 获取用户列表
     */
    public static List<User> getUserList()
    {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "青冘的博客_01", "男", 32, "研发部", BigDecimal.valueOf(1600)));
        userList.add(new User(2, "青冘的博客_02", "男", 30, "财务部", BigDecimal.valueOf(1800)));
        userList.add(new User(3, "青冘的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
        userList.add(new User(4, "青冘的博客_04", "男", 38, "研发部", BigDecimal.valueOf(1500)));
        userList.add(new User(5, "青冘的博客_05", "女", 25, "财务部", BigDecimal.valueOf(1200)));
        return userList;
    }
}

2. Query method

2.1 forEach()

Use forEach() to iterate through list data.

/**
 * 使用forEach()遍历列表信息 by 青冘
 */
@Test
public void forEachTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

The above traversal statement is equivalent to the following statement:

userList.forEach(user -> {System.out.println(user);});

Results of the:

2.2 filter(T -> boolean)

Use filter() to filter list data

Get the list of users whose department is "R&D Department".

/**
 * 使用filter()过滤列表信息 by 青冘
 */
@Test
public void filterTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取部门为“研发部”的用户列表
    userList = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.toList());
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

Results of the:

2.3 findAny() and findFirst()

Use findAny() and findFirst() to get the first piece of data.

Get the user information whose user name is "Qingli's Blog_02", and return null if not found.

/**
 * 使用findAny()获取第一条数据 by 青冘
 */ 
@Test
public void findAnytTest()
{
        //获取用户列表
	    List<User> userList = UserService.getUserList();
	 
	    //获取用户名称为“青冘的博客_02”的用户信息,如果没有找到则返回null
	    User user = userList.stream().filter(u -> u.getName().equals("青冘的博客_02")).findAny().orElse(null);
	 
	    //打印用户信息
	    System.out.println(user);
}

Results of the: 

Note: Both findFirst() and findAny() get the first item of data in the list, but findAny() operates, and the returned elements are uncertain. Calling findAny() multiple times for the same list may return different value. Use findAny() for more efficient performance. If there is less data, in the case of serial, the first result will generally be returned. If it is in the case of parallel (parallelStream), it cannot be guaranteed to be the first result.
 

2.4map(T -> R) and flatMap(T -> Stream)

Use map() to map each element T in the stream to R (similar to type conversion).

Use flatMap() to map each element T in the stream into a stream, and then connect each stream into a stream.

Use the map() method to get the name column in the users list.

/**
 * 使用map()获取列元素 by 青冘
 */
@Test
public void mapTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取用户名称列表
    List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
    //或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
 
    //遍历名称列表
    nameList.forEach(System.out::println);
}

The returned result is an array type, written as follows:

//数组类型
String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);

Results of the:

Use flatMap() to concatenate each element in the stream into a stream.

/**
 * 使用flatMap()将流中的每一个元素连接成为一个流 by 青冘
 */
@Test
public void flatMapTest()
{
    //创建城市
    List<String> cityList = new ArrayList<String>();
    cityList.add("北京;上海;深圳;");
    cityList.add("广州;武汉;杭州;");
 
    //分隔城市列表,使用 flatMap() 将流中的每一个元素连接成为一个流。
    cityList = cityList.stream()
            .map(city -> city.split(";"))
            .flatMap(Arrays::stream)
            .collect(Collectors.toList());
 
    //遍历城市列表
    cityList.forEach(System.out::println);
}

Results of the:

2.5 distinct()

Use the distinct() method to remove duplicate data.

Get a list of departments and remove duplicate data.

/**
 * 使用distinct()去除重复数据 by 青冘
 */
@Test
public void distinctTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取部门列表,并去除重复数据
    List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());
 
    //遍历部门列表
    departmentList.forEach(System.out::println);
}

Results of the:

2.6 limit(long n) 和 skip(long n)

The limit(long n) method is used to return the first n data, and the skip(long n) method is used to skip the first n data.

/**
 * limit(long n)方法用于返回前n条数据
 * skip(long n)方法用于跳过前n条数据 by 青冘
 */
@Test
public void limitAndSkipTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取用户列表,要求跳过第1条数据后的前3条数据
    userList = userList.stream()
            .skip(1)
            .limit(3)
            .collect(Collectors.toList());
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

Results of the:

3. Statistical methods

3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

Use reduce((T, T) -> T) and reduce(T, (T, T) -> T) for combining elements in a stream, such as summing, producting, maximizing, etc.

  @Test
    public void testReduce() {
       	    Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8});
	         //求集合元素之和
		    System.out.println("求集合元素之和");
	        Integer result = stream.reduce(0, Integer::sum);
	        	        System.out.println(result);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求和
	        System.out.println("求和");
	        stream.reduce((i, j) -> i + j).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最大值
	        System.out.println("求最大值");
	        stream.reduce(Integer::max).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最小值
	        System.out.println("求最小值");
	        stream.reduce(Integer::min).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //做逻辑
	        System.out.println("做逻辑");
	        stream.reduce((i, j) -> i > j ? j : i).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求逻辑求乘机
	        System.out.println("求逻辑求乘机");
	        int result2 = stream.filter(i -> i % 2 == 0).reduce(1, (i, j) -> i * j);

	        Optional.of(result2).ifPresent(System.out::println);
    }

Results of the:

3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long) 

int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum); the method of calculating the sum of elements implies the boxing cost, and the map(User::getAge) method changes after It becomes a Stream type, and each Integer has to be unboxed into a primitive type and then summed by the sum method, which greatly affects the efficiency. To solve this problem, Java 8 conscientiously introduces numerical streams IntStream, DoubleStream, and LongStream. The elements in this stream are all primitive data types, namely int, double, and long. 

The stream is converted to a numeric stream:

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream
 //用户列表中年龄的最大值、最小值、总和、平均值
    int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();
    int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();
    int sumVal = userList.stream().mapToInt(User::getAge).sum();
    double aveVal =  userList.stream().mapToInt(User::getAge).average().getAsDouble(); 

3.3 counting() and count()

List data can be counted using counting() and count().

 //统计研发部的人数,使用 counting()方法进行统计
    Long departCount = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.counting());
 
    //统计30岁以上的人数,使用 count()方法进行统计(推荐)
    Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();

3.4 summingInt()、summingLong()、summingDouble()

For computing the sum, takes a function argument.

//计算年龄总和
int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));

3.5 averagingInt()、averagingLong()、averagingDouble()

used to calculate the average

//计算平均年龄
double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));

Guess you like

Origin blog.csdn.net/qq_17798399/article/details/125743944