Java 8 Stream API Learning Summary

The Java 8 API adds a new abstract called Stream, which allows you to process data in a declarative way.The Stream API can greatly increase the productivity of Java programmers, allowing them to write efficient, clean, and concise code.This style treats a collection of elements as a stream that is transported through the pipeline and can be processed on the nodes of the pipeline, such as filtering, sorting, aggregation, and so on.Element streams are processed in the pipeline by an intermediate operation, and the result of the previous processing is obtained by the final operation.

Why do you want to systematically summarize the Java 8 Stream API this time?To put it simply, no matter the performance, we just want to install x and make it better, that's all!

Stream Basics

Technological process

Create Stream_Intermediate Operation of Stream_Final Operation of Stream

Create Stream

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

// Use List to Create Stream
list.stream()

// Creating a stream with one or more elements
Stream.of(T value)
Stream.of(T... values)

// Creating streams using arrays
Arrays.stream(T[] array)

// Create an empty stream
Stream.empty()

// Merge of two streams
Stream.concat(Stream<? extends T> a, Stream<? extends T> b)

// Unordered Infinite Flow
Stream.generate(Supplier<T> s)

// Producing Infinite Flow by Iteration
Stream.iterate(final T seed, final UnaryOperator<T> f)

Intermediate operation of stream

// Element Filtering
filter
limit
skip
distinct

// mapping
map
flatmap

// sort

Final operation of stream

What results do we want to get from the final operation of the stream pair element?

Construct test data

Employee Entity Class

/**
 * Employee Entity Class
 * @author Erwin Feng
 * @since 2020/4/27 2:10
 */
public class Employee {

    /** Employee ID */
    private Integer id;

    /** Employee Name */
    private String name;

    /** Employee salary */
    private Double salary;
    
    /** Construction method, getter and setter, toString */
}

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
    }
]

Stream API Test

filter filtering

Requirements: Find a list of employees with a salary of 5000

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

map mapping

Requirements: Put employees with compensation greater than 5000 in 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());

flatMap Horizontal Mapping

Requirements: Convert multidimensional lists to single-dimensional lists

Explain:

We divide salaries into a list of 1000-3000, 4000-5000 into a list, and 6000-7000 into a list.

Combine the three lists to form a multidimensional list.

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

sorted sort

Requirements: Sort by compensation

// Sort salaries from smallest to largest
List<Employee> employees = list.stream().sorted(Comparator.comparing(Employee::getSalary)).peek(System.out::println).collect(Collectors.toList());

// Compensation Ranking from Large to Small
List<Employee> employees2 = list.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).peek(System.out::println).collect(Collectors.toList());

min minimum

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);
assert employee != null;
Assert.assertEquals(employee.getSalary(), minValue, 0.0);

max maximum

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

Average average

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);

match matching

// Elements in the allMatch collection must satisfy a condition before returning true
// Pay is greater than or equal to 1000
boolean isAllMatch = list.stream().allMatch(employee -> employee.getSalary() >= 1000);
Assert.assertTrue(isAllMatch);

// Any element in the anyMatch collection that satisfies a condition returns true
// Is there compensation greater than or equal to 7000
boolean isAnyMatch = list.stream().anyMatch(employee -> employee.getSalary() >= 7000);
Assert.assertTrue(isAnyMatch);

// No element in the noneMatch collection satisfies the condition to return true
// No compensation less than 1000
boolean isNoneMatch = list.stream().noneMatch(employee -> employee.getSalary() < 1000);
Assert.assertTrue(isNoneMatch);

distinct weight removal

The default distinct() takes no arguments and is weighted according to Object#equals(Object).According to the API, this is an operation with an intermediate state.

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

If we want to weight based on an attribute in an object, we can use StreamEx

// Use StreamEx for weight removal
List<Employee> employees2 = StreamEx.of(list).distinct(Employee::getSalary).collect(Collectors.toList());
Assert.assertEquals(7, employees2.size());

You can also use the JDK Stream API, of course

private static <T>Predicate<T> distinctByKey(Function<? super T, ?> 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());

reduce trim

Requirements: Calculate total compensation

// Convert employee list to compensation list first
// Recalculate total compensation
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);

In addition, we can set the identity value of an accumulative function.

double salarySum5 = list.stream().map(Employee::getSalary).reduce(1.00, Double::sum);
Assert.assertEquals(salarySum5, sum + 1, 0.0);

Termination result of collector stream

// joining Split 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);

count statistics

Requirements: Number of employees with compensation of 5000

Do not use streams

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

Use Stream

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

SummizingDouble 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);

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

partitioningBy partition

It is divided into two zones: true and false.

Requirements: Find employees who are paid 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));

true:[Employee{id=7, name='Jasmine', salary=6000.0}, Employee{id=8, name='Jack', salary=6000.0}, Employee{id=9, name='Poppy', salary=7000.0}]

false:[Employee{id=1, name='Jacob', salary=1000.0}, Employee{id=2, name='Sophia', salary=2000.0}, Employee{id=3, name='Rose', salary=3000.0}, Employee{id=4, name='Lily', salary=4000.0}, Employee{id=5, name='Daisy', salary=5000.0}, Employee{id=6, name='Jane', salary=5000.0}]

groupingBy Grouping

Requirements: Grouped by employee compensation

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

Another example is Salary 1 > Sum (Salary * Number of employees)

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

Parallel parallel computation

Simply put, start multiple threaded calculations

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);
2020-05-15 01:47:14.231 [ForkJoinPool.commonPool-worker-4] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jacob
2020-05-15 01:47:15.226 [ForkJoinPool.commonPool-worker-2] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Sophia
2020-05-15 01:47:16.226 [ForkJoinPool.commonPool-worker-1] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Rose
2020-05-15 01:47:17.226 [ForkJoinPool.commonPool-worker-3] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Lily
2020-05-15 01:47:18.225 [main] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jane
2020-05-15 01:47:18.228 [ForkJoinPool.commonPool-worker-7] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Daisy
2020-05-15 01:47:19.226 [ForkJoinPool.commonPool-worker-5] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jack
2020-05-15 01:47:19.228 [ForkJoinPool.commonPool-worker-6] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Jasmine
2020-05-15 01:47:21.234 [ForkJoinPool.commonPool-worker-4] INFO  com.fengwenyi.study_stream.StreamTest - employee name: Poppy

file File Operation

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

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

Test Code

Study Java 8 Stream API

Learning Links

Tags: Programming Java less Attribute JDK

Posted on Fri, 15 May 2020 14:58:26 -0700 by mduran