The Java 8 API has added a new abstraction called a Stream that lets you process data in a declarative way. The Stream API can greatly improve the productivity of Java programmers, allowing programmers to write efficient, clean, and concise code. This style treats the collection of elements to be processed as a stream that travels through a pipe and can be processed at the nodes of the pipe, such as filtering, sorting, aggregating, and so on. The element stream is processed by intermediate operation in the pipe, and finally the result of the previous processing is obtained by terminal operation.

Why summarize the Java 8 Stream API systematically this time? To put it simply, we don’t care about performance, we just want to install X, and make the X install better, that’s all!

Stream Basics

process

Create a flow → intermediate action for a flow → final action for a flow

Create a flow

What elements do we need to put into the stream? Common apis are:

List.stream (); // Use one or more elements to create stream.of (T value) stream.of (T... Stream (T[] array) // Create an empty stream.empty () // Merge stream.concat (stream <? extends T> a, Stream<? Extends T> b) // Generate (Supplier<T> s) // Iterate to generate an infinite Stream. Iterate (final T seed, iterate) final UnaryOperator<T> f)Copy the code

An intermediate operation of a stream

// Element filterlimitSkip distinct // map flatmap // SortCopy the code

The final operation of the stream

What result do we want from the flow’s final operation on the element

Construct test data

Employee Entity Class

/** * Employee entity class *@author Erwin Feng
 * @since2020/4/27 2:10 * /
public class Employee {

    /** Employee ID */
    private Integer id;

    /** Employee name */
    private String name;

    /** Employee salary */
    private Double salary;
    
    /** constructor, getter and setter, toString */
}
Copy the code

Test data list

[{"id":1."name":"Jacob"."salary":1000
    },
    {
        "id":2."name":"Sophia"."salary":2000
    },
    {
        "id":3."name":"Rose"."salary":3000
    },
    {
        "id":4."name":"Lily"."salary":4000
    },
    {
        "id":5."name":"Daisy"."salary":5000
    },
    {
        "id":6."name":"Jane"."salary":5000
    },
    {
        "id":7."name":"Jasmine"."salary":6000
    },
    {
        "id":8."name":"Jack"."salary":6000
    },
    {
        "id":9."name":"Poppy"."salary":7000}]Copy the code

Stream API Test

The filter to filter

Need: Find a list of employees whose salary is 5000

List<Employee> employees = list.stream().filter(employee -> employee.getSalary() == 5000)
        .peek(System.out::println)
        .collect(Collectors.toList());
Assert.assertEquals(2, employees.size());
Copy the code

The map mapping

Requirement: Put the employee whose salary is more than 5000 into the Leader object

List<Leader> leaders = list.stream().filter(employee -> employee.getSalary() > 5000).map(employee -> {
    Leader leader = new Leader();
    leader.setName(employee.getName());
    leader.setSalary(employee.getSalary());
    return leader;
}).peek(System.out::println).collect(Collectors.toList());
Assert.assertEquals(3, leaders.size());
Copy the code

FlatMap Horizontal mapping

Requirement: Convert a multidimensional list to a one-dimensional list

Description:

We divided the salary between 1000 and 3000 into a list, the salary between 4000 and 5000 into a list, and the salary between 6000 and 7000 into a list.

Combine these three lists to form a multidimensional list.

List<Employee> employees = multidimensionalList.stream().flatMap(Collection::stream).collect(Collectors.toList());
Assert.assertEquals(9, employees.size());
Copy the code

Sorted order

Need: Sort by salary

// Rank your salary in ascending order
List<Employee> employees = list.stream().sorted(Comparator.comparing(Employee::getSalary)).peek(System.out::println).collect(Collectors.toList());

// Salary in ascending orderList<Employee> employees2 = list.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).peek(System.out::println).collect(Collectors. toList());Copy the code

Min min

double minValue = list.stream().mapToDouble(Employee::getSalary).min().orElse(0);
Assert.assertEquals(1000, minValue, 0.0);

Employee employee = list.stream().min(Comparator.comparing(Employee::getSalary)).orElse(null);
assertemployee ! =null;
Assert.assertEquals(employee.getSalary(), minValue, 0.0);
Copy the code

Max maximum

double maxValue = list.stream().mapToDouble(Employee::getSalary).max().orElse(0);
Assert.assertEquals(7000, maxValue, 0.0);
Copy the code

The average business,

double sum = list.stream().mapToDouble(Employee::getSalary).sum();
double averageValue = list.stream().mapToDouble(Employee::getSalary).average().orElse(0);
Assert.assertEquals(sum / list.size(), averageValue, 0.0);
Copy the code

Match match

// All elements in the allMatch collection must meet the condition to return true
// The salary is 1000 or more
boolean isAllMatch = list.stream().allMatch(employee -> employee.getSalary() >= 1000);
Assert.assertTrue(isAllMatch);

// The anyMatch set returns true as long as one of the elements meets the criteria
// Is there any salary greater than or equal to 7000
boolean isAnyMatch = list.stream().anyMatch(employee -> employee.getSalary() >= 7000);
Assert.assertTrue(isAnyMatch);

// Return true if there are no elements in the noneMatch collection
// No salary less than 1000
boolean isNoneMatch = list.stream().noneMatch(employee -> employee.getSalary() < 1000);
Assert.assertTrue(isNoneMatch);
Copy the code

Distinct to heavy

The default distinct() does not accept arguments and is de-weighted based on Object#equals(Object). According to the API, this is an operation with intermediate states.

List<Employee> employees = list.stream().distinct().collect(Collectors.toList());
Assert.assertEquals(9, employees.size());
Copy the code

We can use StreamEx if we want to de-duplicate an object based on an attribute

// Use StreamEx to remove weights
List<Employee> employees2 = StreamEx.of(list).distinct(Employee::getSalary).collect(Collectors.toList());
Assert.assertEquals(7, employees2.size());
Copy the code

You can also use the JDK Stream API

private static <T>Predicate<T> distinctByKey(Function<? superT, ? > keyExtractor) {
    Map<Object, Boolean> result = new ConcurrentHashMap<>();
    return t -> result.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

List<Employee> employees3 = list.stream().filter(distinctByKey(Employee::getSalary)).collect(Collectors.toList());
Assert.assertEquals(7, employees3.size());
Copy the code

Reduce cutting

Requirement: Calculate the sum of salary

// Convert the employee list to the salary list
// Then calculate the total salary
double salarySum = list.stream().map(Employee::getSalary).reduce(Double::sum).orElse(0.0);
double sum = list.stream().mapToDouble(Employee::getSalary).sum();
Assert.assertEquals(salarySum, sum, 0.0);
Copy the code

Alternatively, we can set an identifier value for the summation function

double salarySum5 = list.stream().map(Employee::getSalary).reduce(1.00, Double::sum);
Assert.assertEquals(salarySum5, sum + 1.0.0);
Copy the code

Termination result of the Collector flow

// Joining string
String employeeNames = list.stream().map(Employee::getName).collect(Collectors.joining(","));
System.out.println(employeeNames); // Jacob, Sophia, Rose, Lily, Daisy, Jane, Jasmine, Jack, Poppy

// Return a List
List<String> employeeNameList = list.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(employeeNameList);

// return a Set
Set<String> employeeNameSet = list.stream().map(Employee::getName).collect(Collectors.toSet());
System.out.println(employeeNameSet);

// Return a Vector
Vector<String> employeeNameVector = list.stream().map(Employee::getName).collect(Collectors.toCollection(Vector::new));
System.out.println(employeeNameVector);

// Return a Map
Map<Integer, String> employeesMap = list.stream().collect(Collectors.toMap(Employee::getId, Employee::getName));
System.out.println(employeesMap);
Copy the code

The count statistics

Need: Number of employees with a salary of 5000

Do not use the flow

int count2 = 0;
for (Employee employee : list) {
    if (employee.getSalary() == 5000) {
        count2++;
    }
}
System.out.println(count2);
Copy the code

Use the stream

long count3 = list.stream().filter(employee -> employee.getSalary() == 5000).count();
Assert.assertEquals(count3, count2);
Copy the code

SummarizingDouble statistical analysis

DoubleSummaryStatistics employeeSalaryStatistics = list.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("employee salary statistics:" + employeeSalaryStatistics);

DoubleSummaryStatistics employeeSalaryStatistics2 = list.stream().mapToDouble(Employee::getSalary).summaryStatistics();
System.out.println("employee salary statistics2:" + employeeSalaryStatistics2);
Copy the code

{count=9, sum=39000.000000, min=1000.000000, average=4333.333333, Max =7000.000000}

PartitioningBy partition

It is divided into two sections that satisfy the condition (true) and that do not satisfy the condition (false)

Need: Find the employee whose salary is more than 5000

Map<Boolean, List<Employee>> map = list.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000));
System.out.println("true:" + map.get(Boolean.TRUE));
System.out.println("false:" + map.get(Boolean.FALSE));
Copy the code

True :[Employee{id=7, name=’Jasmine’, salary=6000.0}, Employee{id=8, name=’Jack’, salary=6000.0}, Employee{id=8, name=’Jack’, salary=6000.0}, Name = ‘Poppy, salary = 7000.0}]

False :[Employee{id=1, name=’Jacob’, salary=1000.0}, Employee{id=2, name=’Sophia’, salary=2000.0}, Employee{id=2, name=’Sophia’, salary=2000.0}, Name =’Rose’, salary=3000.0}, Employee{id=4, name=’Lily’, salary=4000.0}, Employee{id=5, name=’Daisy’, salary=5000.0}, The Employee {id = 6, name = ‘Jane’ salary = 5000.0}]

GroupingBy grouping

Requirements: Group employees according to salary

Map<Double, List<Employee>> map = list.stream().collect(Collectors.groupingBy(Employee::getSalary));
System.out.println(map);
Copy the code

Another example: Salary 1 > Total (Salary * number of employees)

Map<Double, Double> map3 = list.stream().collect(Collectors.groupingBy(Employee::getSalary, Collectors.summingDouble(Employee::getSalary)));
System.out.println(map3);
Copy the code

Parallel computing

Simply put, this is to start multiple threads of computation

private static void cal(Employee employee) {
    try {
        long sleepTime = employee.getSalary().longValue();
        TimeUnit.MILLISECONDS.sleep(sleepTime);
        logger.info("employee name: {}", employee.getName());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

list.stream().parallel().forEach(StreamTest::cal);
Copy the code
2020-05-15 01:47:14.231 [ForkJoinPool.com monpool-worker-4] infom.fengwenyi. Study_stream. streamtest-employee name: Jacob 2020-05-15 01:47:15.226 [ForkJoinPool.com monpool-worker-2] infom.fengwenyi. Study_stream.streamtest-employee name: Sophia 2020-05-15 01:47:16.226 [ForkJoinPool.com monpool-worker-1] info.fengwenyi. Study_stream. streamtest-employee name: Rose 2020-05-15 01:47:17.226 [ForkJoinPool.com monpool-worker-3] infom.fengwenyi. Study_stream.streamtest-employee Name: Lily 2020-05-15 01:47:18.225 [main] info.fengwenyi. Study_stream. streamtest-employee name: Jane 2020-05-15 01:47:18.228 [ForkJoinPool.com monpool-worker-7] infom.fengwenyi. Study_stream.streamtest-employee name: Daisy 2020-05-15 01:47:19.226 [ForkJoinPool.com monpool-worker-5] infom.fengwenyi. Study_stream.streamtest-employee name: Jack 2020-05-15 01:47:19.228 [ForkJoinPool.com monpool-worker-6] infom.fengwenyi. Study_stream.streamtest-employee name: Jasmine 2020-05-15 01:47:21.234 [ForkJoinPool.com monpool-worker-4] infom.fengwenyi. Study_stream.streamtest-employee  name: PoppyCopy the code

File Operation

try (PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(Paths.get(tempFilePath)))) { // Use try to automatically close the stream
    list.forEach(printWriter::println);
    list.forEach(employee -> printWriter.println(employee.getName())); // Write the employee's name to the file
}

// Read the employee's name from the file
List<String> s = Files.lines(Paths.get(tempFilePath)).peek(System.out::println).collect(Collectors.toList());
Copy the code

The test code

Study Java 8 Stream API

Learning links

  • Noodlespan > Stream series
  • The Streams API in Java 8
  • New in Java8 -Stream API common full version
  • Stream In Java