The road to java learning 10 -- new features of Java 8

What's new in JAVA8

  • Faster
  • Less code - Lambda
  • Powerful Stream API
  • Easy parallel
  • Minimize null pointer exceptions - optional

Zero, faster

Faster means that the structure of hashMap adds red and black trees.

In the previous hashmap structure, jdk adopts the method of array + linked list. When collision occurs, the efficiency of searching will be greatly reduced. Therefore, in jdk1.8, the method of array + linked list + red black tree is adopted. The premise is that when the number of collisions is greater than 8 and the total capacity is greater than 64, the linked list structure will be converted into red black tree. At this time, the efficiency of adding elements will be reduced, but the efficiency of searching and deleting will be greatly improved:

1, Lambda expression

In mathematics, a function is a set of calculation schemes with input and output, that is, "what to do with". In contrast, the object-oriented pair overemphasizes that "things must be done in the form of objects", while the functional idea tries to ignore the complex object-oriented syntax - emphasizing what to do, not in what form.

  • Object oriented thinking: do a thing, find an object that can solve it, call the method of the object, and complete the thing.
  • Functional programming thought: as long as the results can be obtained, it doesn't matter who does it or how to do it. What matters is the results and doesn't pay attention to the process.

Lambda is an anonymous function. We can understand lambda expression as a piece of code that can be passed (pass the code like data). You can write more concise and flexible code. As a more compact code style, the language expression ability of Java has been improved.

//Original anonymous inner class
@Test
public void test1(){
    // Anonymous inner classes, that is, classes that only exist without instance objects
    Comparator<String> com = new Comparator<String>(){
        @Override
        public int compare(String o1, String o2) {
            // This sentence is the core code
            return Integer.compare(o1.length(), o2.length());
        }
    };
    TreeSet<String> ts = new TreeSet<>(com);
    
}

//Current Lambda expression
@Test
public void test2(){
    // Simplify code writing and extract the core code
    // Lambda expressions are passed as arguments
    Comparator<String> com = (x, y) -> Integer.compare(x.length(), y.length());
    TreeSet<String> ts = new TreeSet<>(com);
}

1.1 syntax

Lambda expressions introduce a new syntax element and operator into the Java language. This operator is called "- >", which is called lambda operator or arrow operator. It divides lambda into two parts:

  • Left: all parameters required by Lambda expression are specified
  • Right: Specifies the Lambda body, that is, the function to be performed by the Lambda expression

1.1.1 no parameter, no return value

1.1.2 one parameter, no return value

1.1.3 if there is only one parameter, parentheses can be omitted

1.1.4 more than two parameters have return values

1.1.5 if there is only one statement in Lambda body, return and braces can be omitted

1.1.6 the data type of the parameter list can be omitted

The parameter types in the above Lambda expressions are inferred by the compiler. There is no need to specify the type in the Lambda expression, and the program can still be compiled. This is because the javac compiler infers the type of the parameter in the background according to the context of the program. The type of Lambda expression depends on the context and is inferred by the compiler. This is called "type inference".

1.2 advantages and disadvantages

1.1.1 advantages

  • Concise.

  • Very easy parallel computing.

  • May represent future programming trends.

  • Combined with hashmap's computeIfAbsent method, recursive operation is very fast. java has special optimizations for recursion.

1.1.2 disadvantages

Because too much is omitted, the readability of the code may be reduced to some extent.

1.3 implementation

Copy parentheses, write dead right arrows, landing braces

package com.zdp.java8;

import org.junit.Test;

import java.util.Comparator;
import java.util.function.Consumer;

/**
 * 1, Basic syntax of Lambda expression: a new operator "- >" has been introduced in Java 8, which is called arrow operator or Lambda operator
 * The arrow operator splits the Lambda expression into two parts:
 * <p>
 * Left: parameter list of Lambda expression
 * Right: the function to be performed in the Lambda expression, that is, the Lambda body
 * <p>
 * Syntax format 1: no parameter, no return value
 * () -> System.out.println("Hello Lambda!");
 * <p>
 * Syntax format 2: there is a parameter and no return value
 * (x) -> System.out.println(x)
 * <p>
 * Syntax format 3: if there is only one parameter, parentheses can be omitted
 * x -> System.out.println(x)
 * <p>
 * Syntax format 4: there are more than two parameters, return values, and multiple statements in the Lambda body
 * Comparator<Integer> com = (x, y) -> {
 * System.out.println("Functional interface "");
 * return Integer.compare(x, y);
 * };
 * <p>
 * Syntax format 5: if there is only one statement in Lambda body, return and braces can be omitted
 * Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
 * <p>
 * Syntax format 6: the data type of the parameter list of Lambda expression can be omitted, because the JVM compiler infers the data type through the context, that is, "type inference"
 * (Integer x, Integer y) -> Integer.compare(x, y);
 * <p>
 * Couplet above: a parenthesized province is encountered on the left and right
 * Second line: infer type province on the left
 * Banner: save if you can
 * <p>
 * 2, Lambda expressions require support for functional interfaces
 * Functional interface: an interface in which there is only one abstract method is called a functional interface. It can be modified with the annotation @ functional interface
 * The @ FunctionalInterface annotation can check whether it is a functional interface
 */
public class LambdaStudy01 {
    // The Lambda body is used to implement the interface
    // The premise is that the interface contains only one abstract method (functional interface)
    @Test
    public void test1() {
        // Local inner class of the same level
        int num = 0;//Before jdk 1.7, it must be final

        // Runnable is an interface with no parameters and no return value
        Runnable r = new Runnable() {
            @Override
            public void run() {
                // Anonymous inner classes refer to local inner classes of the same level
                System.out.println("Hello World!" + num);
            }
        };
        r.run();
        System.out.println("-------------------------------");

        // Syntax format 1: no parameter, no return value
        Runnable r1 = () -> System.out.println("Hello Lambda!");
        r1.run();
    }

    // Syntax format 2: there is a parameter and no return value
    @Test
    public void test2() {
        /**
         * Consumer Is a functional interface provided in JAVA8
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         *
         * There is a parameter method inside
         * void accept(T t);
         */
        // Lambda expression is the only method to implement this interface
        Consumer<String> consumer = (x) -> System.out.println(x);
        consumer.accept("zdp fucking great!!!");
    }

    // Syntax format 3: if there is only one parameter, parentheses can be omitted
    @Test
    public void test3() {
        Consumer<String> consumer = x -> System.out.println(x);
        consumer.accept("zdp fucking great!!!");
    }

    // Syntax format 4: there are more than two parameters, return values, and multiple statements in the Lambda body
    @Test
    public void test4() {
        // Implement functional interfaces
        Comparator<Integer> comparator = (a, b) -> {
            // Multiple statements
            System.out.println("Functional interface");
            return Integer.compare(a, b);
        };

        // implement
        int compare = comparator.compare(1, 2);
        System.out.println(compare);
    }

    // Syntax format 5: if there is only one statement in Lambda body, return and braces can be omitted
    @Test
    public void test5() {
        // Implement a functional interface with only one statement
        // Syntax format 6: the data type of the parameter list of Lambda expression can be omitted
        // Because the JVM compiler infers the data type through the context, that is, "type inference"
        Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);

        // implement
        int compare = comparator.compare(1, 2);
        System.out.println(compare);
    }
}

2, Functional interface

  • A functional interface has only one abstract method that has not been overridden. However, the method in Object does not count, nor do the default method and static method in the interface
/**
 * Functional interface
 * A functional interface has and has only one abstract method that is not overridden.
 * However, the method in the Object does not count, and the default method in the interface does not count
 * The annotation @ FunctionalInterface modifier checks whether it is a functional interface
 */
@FunctionalInterface
public interface FunctionalInterfaceTest {
    // Contains only one abstract method
    void test();

    // Except the default implementation method
    default void defaultTest(){
        System.out.println("This is the default implementation of functional interfaces");
    }
    
    // Except static methods
    static void staticTest(){
        System.out.println("This is a static implementation of functional interfaces");
    }

    // Object method
    String toString();
    int hashCode();
    boolean equals(Object object);
}
  • Use the annotation @ functional interface modifier to check whether it is a functional interface

default method:

java8 adds the default method to the interface in order to add functions to the existing class library without affecting their implementation classes. Imagine that if the default implementation is not added, all implementation classes of the interface must implement this method again, which will cause compatibility problems. If the default implementation is defined, the implementation class can call directly without implementing this method.

// Enhanced interface
public interface MyInterface01 {
    // Use the default keyword to add the default implementation of the interface method, which can also be overridden by the implementation class
       default  void testInterFace(){
           System.out.println("Default methods for enhanced interfaces");
       }
       void test();
}

Note: if the default method in the interface cannot meet the needs of an implementation class, the implementation class can override the default method. Do not add the default keyword, for example:

// Implementation class of enhanced interface
public class InterfaceTest implements MyInterface01 {
//    //Override the default method of the interface or not
//    @Override
//    public void testInterFace() {
//
//    }

    @Override
    public void test() {
        System.out.println("Enhanced interface test");
    }
   }

2.1 user defined functional interface

/**
 * Functional interface
 * A functional interface has and has only one abstract method that is not overridden.
 * However, the method in the Object does not count, and the default method in the interface does not count
 * The annotation @ FunctionalInterface modifier checks whether it is a functional interface
 */
@FunctionalInterface
public interface FunctionalInterfaceDemo {
    // Contains only one abstract method
    Integer test(int x);

    // Except the default implementation method
    default void defaultTest(){
        System.out.println("This is the default implementation of functional interfaces");
    }

    // Object method
    String toString();
    int hashCode();
    boolean equals(Object object);
}

2.1.1 general implementation

/**
 * Implementation of functional interface using Lambda expression
 */
public class LambdaStudy02 {
    @Test
    public void test1(){
        // Implementation of functional interface using Lambda body
        FunctionalInterfaceDemo demo = x -> x*x;

        // Executing methods in interfaces
        Integer result = demo.test(10);

        System.out.println(result.intValue());
    }
}

2.1.2 parameter transfer Lambda expression

In order to pass a Lambda expression as a parameter, the parameter type of the received Lambda expression must be the type of a functional interface compatible with the Lambda expression.

@Test
public void test2(){
    // Call the method myFun, where the second parameter is a Lambda expression
    Integer result = myFun(100, x -> x + x);
    System.out.println(result);
}

/**
 * Execute the method in the functional interface and return the result
 * @param num
 * @param demo  Parameter is Lambda body (implementation of method in functional interface)
 * @return
 */
public Integer myFun(Integer num,FunctionalInterfaceDemo demo){
    return demo.test(num);
}

2.2 core functional interface

Functional interfaces can be customized, and JAVA also provides four core functional interfaces.

/**
 * For the core functional interface, the parameters on the left side of the lambda expression are determined according to the parameters of the methods in each interface
 * Consumer<T> : Consumer interface
 * void accept(T t);
 * <p>
 * Supplier<T> : Supply type interface
 * T get();
 * <p>
 * Function<T, R> : Functional interface
 * R apply(T t);
 * <p>
 * Predicate<T> : Assertive interface
 * boolean test(T t);
 */
public class LambdaStudy04 {

    // Consumption type
    @Test
    public void test1() {
        consumerTest(10000.0, x -> {
            x = x - 500;
            System.out.println("surplus" + x + "element");
        });
    }

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     *          Consumer Source code
     *          void accept(T t);
     */
    public void consumerTest(Double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }

    // Supply type
    @Test
    public void test2() {
        // The parameter is the implementation of the parameterless method get() of the supply interface. Since there is no parameter, the () empty bracket is used
        List<Integer> result = supplierTest(10, () -> (int) (Math.random() * 100));

        for (Integer num : result) {
            System.out.println(num);
        }
    }

    // Requirement: generate a specified number of integers and put them into the set
    public List<Integer> supplierTest(int num, Supplier<Integer> supplier) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            // Call the method inside the supply interface
            // The function of supplier.get() at this time is to generate an integer
            list.add(supplier.get());
        }
        return list;
    }

    // Functional type
    @Test
    public void test3() {
        // The implementation is to remove spaces
        String s = functionTest("\t\t\t\t lllllllllllllllllsaaaaaa", (str) -> str.trim());
        System.out.println(s);
    }

    //Requirements: for handling strings
    public String functionTest(String str, Function<String, String> fun){
        // The apply method specifies what to do with the string, and its specific implementation is defined in lambda
        return fun.apply(str);
    }

    //Predicate < T > assertion interface:
    @Test
    public void test4(){
        List<String> list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok");
        // Define filter criteria
        List<String> strList = predicateTest(list, (s) -> s.length() > 3);

        for (String str : strList) {
            System.out.println(str);
        }
    }
    //Requirement: put the strings that meet the conditions into the collection
    public List<String> predicateTest(List<String> list, Predicate<String> pre){
        List<String> strList = new ArrayList<>();

        for (String str : list) {
            // What is the specific condition? It is defined in the lambda expression
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }

}

Take the Consumer interface as an example:

3, Method reference and constructor reference

3.1 method reference

When the operation to be passed to the Lambda body already has an implemented method, you can use the method reference!

The parameter list of the implementation abstract method must be consistent with the parameter list of the method reference method! This is why parameters can be omitted in method references.

Method reference: use the operator "::" to separate the method name from the name of the object or class. There are three main uses:

  • Object:: instance method
  • Class:: static method
  • Class:: instance method

// Object reference:: instance method name
@Test
public void test1() {

    // Call the consumer core functional interface
    //1 original lambda implementation
    Consumer<String> consumer = (x) -> System.out.println(x);
    consumer.accept("Zhang Da Pao 666");

    //2 method reference implementation
    // Because the println() method is an instance method, that is, it has been implemented
    // And void println(String x) is a method with parameters and no return value, so it can correspond to the accept method of the consumer interface
    Consumer<String> consumer1 = System.out::println;
    consumer1.accept("dddyyy6666666");
}

@Test
public void test2() {
    Employee employee = new Employee();

    // Call the supplier interface. T get() is a method with no parameters and return value
    //1. In the original implementation, there is only one sentence in the method body, and return is omitted
    Supplier<String> supplier = () -> employee.getName();
    String name1 = supplier.get();
    System.out.println(name1);

    //2 method reference
    // The getAge() method has been implemented in Employee
    Supplier<Integer> supplier1 = employee::getAge;
    Integer age = supplier1.get();
    System.out.println(age);
}

3.2 constructor reference

The parameter list of the constructor needs to be consistent with the parameter list in the functional interface!

  • Class name:: new
// Constructor reference
// Note: the parameter list of the constructor to be called should be consistent with the parameter list of the abstract method in the functional interface
@Test
public void test7() {
    // Constructor that calls a parameter of Employee
    Function<String, Employee> fun = Employee::new;

    // Constructor calling two parameters of Employee
    BiFunction<String, Integer, Employee> fun2 = Employee::new;
}

@Test
public void test6() {
    Supplier<Employee> sup = () -> new Employee();
    System.out.println(sup.get());

    System.out.println("------------------------------------");

    // Because the get method of the supplier is parameterless, it automatically matches the parameterless constructor of the Employee class
    Supplier<Employee> sup2 = Employee::new;
    System.out.println(sup2.get());
}

4, Stream API

For Stream processing, the first thing to clarify is that Stream in java8 is completely different from I/O streams InputStream and OutputStream. The Stream mechanism is an enhancement to the collection iterator. Streams allow you to process data sets declaratively.

In the process of transmitting data sources (sets, arrays, etc.), the Stream mechanism will perform a series of intermediate operations on them, but the final data source will not change, and a new Stream stream will be generated.

Collection is about data, and flow is about computation

The operation of a Stream is as shown in the figure above. In a pipeline, it is divided into three steps:

  • The first step is to create a Stream and obtain a Stream from a set and array;
  • The second step is the intermediate operation chain to process the data;
  • The third step is to terminate the operation, which is used to execute the intermediate operation chain and return the result.

4.1 create flow

4.1.1 create from collection

The Collection interface in Java 8 is extended to provide two methods for obtaining streams. These two methods are default methods, that is, all interfaces implementing the Collection interface can be used directly without implementation:

  • Default stream (): returns a sequential stream.

  • default Stream parallelStream(): returns a parallel stream.

List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);

Stream<Integer> stream = integerList.stream();				//Sequential flow
Stream<Integer> stream1 = integerList.parallelStream();		//Parallel stream

4.1.2 create from array

The static method stream() of Arrays in Java 8 can obtain the array stream:

  • Static stream (t [] array): returns a stream

  • Overloaded form, which can handle arrays of corresponding basic types:

    public static IntStream stream(int[] array)
    public static LongStream stream(long[] array)
    public static DoubleStream stream(double[] array)

int[] intArray = {1,2,3};
IntStream stream = Arrays.stream(intArray);

4.1.3 create by value

You can create a stream by displaying values using the static method Stream.of(). It can receive any number of parameters.

  • public static Stream of(T... values): returns a stream.
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);

4.1.4 create by function

You can create an infinite stream using the static methods Stream.iterate() and Stream.generate().

  • Iteration: public static Stream iterate(final T seed, final UnaryOperator f)

    // The parameters of the iterate method are: seed and a functional interface
    Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2);
    //Note: the use of infinite flow must be truncated with limit (intermediate operation), otherwise it will be created without limit
    stream3.limit(10).forEach(System.out::println);
    

  • Generate: public static stream generate (suppliers)

// The parameter of the generate method is the Supplier core functional interface
Stream<Double> stream4 = Stream.generate(() -> Math.random() * 100);
stream4.limit(20).forEach(System.out::println);

4.2 intermediate operation

If only the intermediate operation of the Stream will not be executed, the intermediate operation will be executed when the terminal operation is executed. This method is called delayed loading or lazy evaluation. Multiple intermediate operations form an intermediate operation chain. Unless the termination operation is triggered on the pipeline, the intermediate operation will not perform any processing. The intermediate operation chain will be executed once only when the terminal operation is executed. At this time, all processing will be performed at one time.

4.2.1 screening and slicing

(1)filter()

The filter method receives a Lambda expression corresponding to the Predicate function and returns a Boolean value to filter some elements from the stream.

@Test
public void test1() {
    // Create a stream in collection mode
    Stream<Employee> stream = emps.stream();

    /**
     * Intermediate operation
     * 1 filter:
     *   The parameter is a Predicate assertion core functional interface, which has parameters and returns boolean value
     *   The return value is a new stream
     * Demand: filter those older than 30
     */
    Stream<Employee> stream1 = stream.filter(employee -> {
        System.out.println("Intermediate operation: filtering");
        return employee.getAge() > 30;
    });

    // Terminate operation: if the termination operation is not executed, the previous intermediate operation will not be executed
    // Inert evaluation
    stream1.forEach(System.out::println);

}
(2)distinct()

The duplicate elements are removed by hashCode() and equals() of the elements generated by the stream.

@Test
public void test3(){
    /**
     * Intermediate operation
     * distinct: Weight remover
     * Remove duplicate elements by hashCode() and equals() of the elements generated by the stream
     * Therefore, the hashCode() and equals() methods should be overridden in the employee class
     */
    emps.stream().distinct().forEach(System.out::println);
}
// employee overrides the hashCode() and equals() methods and uses IDEA to generate automatically
@Override
public int hashCode() {
   final int prime = 31;
   int result = 1;
   result = prime * result + age;
   result = prime * result + id;
   result = prime * result + ((name == null) ? 0 : name.hashCode());
   long temp;
   temp = Double.doubleToLongBits(salary);
   result = prime * result + (int) (temp ^ (temp >>> 32));
   return result;
}

@Override
public boolean equals(Object obj) {
   if (this == obj)
      return true;
   if (obj == null)
      return false;
   if (getClass() != obj.getClass())
      return false;
   Employee other = (Employee) obj;
   if (age != other.age)
      return false;
   if (id != other.id)
      return false;
   if (name == null) {
      if (other.name != null)
         return false;
   } else if (!name.equals(other.name))
      return false;
   if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
      return false;
   return true;
}
(3)limit()

Truncate the stream so that its elements do not exceed the given number. If the number of elements is less than maxSize, all elements are obtained.

@Test
public void test2() {
    // Create a stream in collection mode
    Stream<Employee> stream = emps.stream();

    /**
     * Intermediate operation
     * limit: Chopper
     */
    Stream<Employee> stream1 = stream.filter(employee -> employee.getSalary() > 6000)
                                     .limit(3);

    // Terminate operation: if the termination operation is not executed, the previous intermediate operation will not be executed
    stream1.forEach(System.out::println);
}
(4)skip()

Skip elements and return a stream that throws away the first n elements. If there are less than n elements in the stream, an empty stream is returned. Complementary to limit(n).

@Test
public void test4(){
    /**
     * Intermediate operation
     * skip: Skip elements and return a stream that throws away the first n elements. If there are less than n elements in the stream, an empty stream is returned. Complementary to limit(n)
     */
    emps.parallelStream()
            .filter((e) -> e.getSalary() >= 5000)
            .skip(2)
            .forEach(System.out::println);
}

4.2.2 mapping processing

(1)map()

Receive a Function function as an argument, which will be applied to each element and mapped to a new element. That is, conversion operations. map also has three methods applied to specific types: mapToInt, mapToLong and mapToDouble. These three methods are also easy to understand. For example, mapToInt is to convert the original Stream into a new Stream. The elements in the newly generated Stream are of type int. These three methods can avoid the additional consumption of automatic packing / unpacking.

Note: the Function that receives a Function in the map: R apply (T); There needs to be a return value inside, so the return value should be set in the lambda expression in the map.

@Test
public void test5() {
    /**
     * Intermediate operation
     * mapping
     * map-Receive Lambda, convert elements into other forms or extract information
     * Receive a function as an argument, which is applied to each element and mapped to a new element
     */
    List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
    Stream<String> stream = strList.stream()
            .map(str -> str.toUpperCase());		// Each data in the stream is capitalized [omit [return] str.toUpperCase()]
    stream.forEach(System.out::println);

    System.out.println("-----------lambda expression/Method reference-------------------");

    //1 use map to extract the names in the stream
    emps.stream()
            // A Function function is received in the map, and a return value is required
            .map(employee -> employee.getName())    // An ordinary lambda expression has only one sentence, so return can be omitted
            .forEach(System.out::println);          // Method reference

    //2 use map to extract the names in the stream
    emps.stream()
            // A Function function is received in the map, and a return value is required
            .map(Employee::getName)    // Method reference using class name:: instance method
            .forEach(System.out::println);          // Method reference

    System.out.println("--------------Nesting of streams-----------------");

    strList.stream()
            .map(StreamAPI_Mid::getCharacter)       // Returns a stream object
            .forEach(sm -> {                        // The first layer traverses the stream
                sm.forEach(System.out::println);    // Therefore, you need to traverse one layer to get the characters
            });                              // Return effect: {a,a,a},{b,b,b},{c,c,c}...}

    System.out.println("----------flatMap-------------------");

    strList.stream()
            .flatMap(StreamAPI_Mid::getCharacter)       // Returns a stream object
            .forEach(System.out::println);              // flatMap only needs one layer of traversal
                                                   // Return effect: {a, a, a, B, B, C, C, C...}
}

// Traverse a String of type String, extract each character and convert it into a stream
public static Stream<Character> getCharacter(String string) {
    List<Character> list = new ArrayList<>();

    for (Character ch : string.toCharArray())
        list.add(ch);

    return list.stream();
}
(2)flatMap()

Receive a Function function as a parameter, convert each value in the stream into another stream, and then connect all streams into one stream. flatMap also has three methods applied to specific types: flatMapToInt, flatMapToLong and flatMapToDouble. The three derived methods used on map are the same.

4.2.3 sorting

(1)sorted()
  • Natural sorting: that is, use comparable
  • Comparator sorting: that is, use comparator to sort user-defined comparators
/**
 * 3 Sorting: sorted
 */
@Test
public void test6() {
    List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");

    // Sort naturally and call the comparableTo method defined inside the String class
    strList.stream()
            .sorted()
            .forEach(System.out::println);

    System.out.println("-----------------------------------");

    // Comparator sort
    emps.stream()
            .sorted((e1, e2) -> {    // Two parameters are required
                if (e1.getAge() == e2.getAge()) {
                    return e1.getName().compareTo(e2.getName());
                } else {
                    return Integer.compare(e1.getAge(),e2.getAge());
                }
            }).forEach(System.out::println);

}

4.3 termination

The terminal operation executes the intermediate operation chain and returns the result. The terminal operation generates results from the pipeline of the stream. The result can be any value that is not a stream, such as List, Integer, or even void.

4.3.1 find and match

package com.zdp.java8.StreamAPI;

import com.zdp.java8.Employee;
import com.zdp.java8.Employee.Status;
import org.junit.Test;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;

/**
 * StreamAPI Terminate operation
 * 1,Find and match
 * 2,reduction
 * 3,collect
 */
public class StreamAPI_Final {

    List<Employee> emps = Arrays.asList(
            new Employee(102, "Li Si", 79, 6666.66, Status.BUSY),
            new Employee(101, "Zhang San", 18, 9999.99, Status.FREE),
            new Employee(103, "Wang Wu", 28, 3333.33, Status.VOCATION),
            new Employee(104, "Zhao Liu", 8, 7777.77, Status.BUSY),
            new Employee(104, "Zhao Liu", 8, 7777.77, Status.FREE),
            new Employee(104, "Zhao Liu", 8, 7777.77, Status.FREE),
            new Employee(105, "pseudo-ginseng", 38, 5555.55, Status.BUSY)
    );

    /**
     * Terminate operation
     * allMatch-Check that all elements match
     * anyMatch-Check to see if at least one element matches
     * noneMatch-Check if there are no matching elements
     * findFirst-Returns the first element
     * findAny-Returns any element in the current stream
     * count-Returns the total number of elements in the stream
     * max-Returns the maximum value in the stream
     * min-Returns the minimum value in the stream
     */
    @Test
    public void test1() {

        // Check that all elements match
        boolean bl = emps.stream()
                .allMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(bl);

        // Check to see if at least one element matches
        boolean bl1 = emps.stream()
                .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(bl1);

        // Check if there are no matching elements
        boolean bl2 = emps.stream()
                .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(bl2);

        // Returns the first element
        Optional<Employee> first = emps.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))     // Lowest paid
                .findFirst();
        // first.orElse(new Employee());
        Employee employee = first.get();
        System.out.println(employee);

        // Return any element in the current stream (filter out all idle people first)
        Optional<Employee> any = emps.stream()
                .filter(employee1 -> employee1.getStatus().equals(Status.FREE))
                .findAny();
        Employee employee1 = any.get();
        System.out.println(employee1);
        
    }

    // Statistics class termination
    @Test
    public void test2() {

        // Returns the total number of elements in the stream
        long count = emps.stream()
                .count();
        System.out.println(count);

        // Returns the maximum value
        Optional<Employee> max = emps.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());

        // Return minimum wage
        Optional<Double> min = emps.stream()
                .map(Employee::getSalary)   // Withdraw the salary of all employees
                .min(Double::compare);      // Use method reference
        System.out.println(min.get());
    }

}

4.3.2 reduction

/**
 * 2,reduction
 * reduce(T identity, BinaryOperator) / reduce(BinaryOperator) -You can combine the elements in the flow repeatedly to get a value.
 */
@Test
public void test3() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

    // Because of the initial value identity, integer is returned
    Integer sum = list.stream()
            .reduce(10, (x, y) -> x + y);// Rewrite the reduced two parameter method
    System.out.println(sum);

    System.out.println("----------------------------------------");

    // Since the return value may be empty, the return type is Optional
    Optional<Double> op = emps.stream()
            .map(Employee::getSalary)
            .reduce(Double::sum);       // Rewrite the method of one parameter of reduction
    System.out.println(op.get());
}

4.3.3 collection

The implementation of the method in the Collector interface determines how to perform collection operations (such as collecting List, Set and Map). However, the Collectors utility class provides many static methods to easily create common Collector instances. The specific methods and instances are shown in the following table:

Note: the data collected is the data in the new stream

/**
 * 3,collect
 * collect-Convert the Stream to another form. Receive the implementation of a Collector interface, which is used to summarize the elements in the Stream
 */
@Test
public void test4(){
    List<String> list = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toList());
    list.forEach(System.out::println);

    System.out.println("----------------------------------");

    Set<String> set = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toSet());
    set.forEach(System.out::println);

    System.out.println("----------------------------------");

    HashSet<String> hs = emps.stream()
            .map(Employee::getName)
            .collect(Collectors.toCollection(HashSet::new));
    hs.forEach(System.out::println);
    
    System.out.println("----------------------------------");
    List<Map<String, Double>> mapList = emps.stream()
        .map((employee) -> {
            Map<String, Double> map = new HashMap<>();
            map.put(employee.getName(), employee.getSalary());
            return map;                                 // Generate hashMap
        }).collect(Collectors.toList());               // Collect hashMap into List
    mapList.forEach(System.out::println);
}

// Statistical collection
@Test
public void test5(){

    // total
    Long count = emps.stream()
            .collect(Collectors.counting());
    System.out.println(count);

    // average value
    Double aver = emps.stream()
            .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(aver);

    // the sum
    Double sum = emps.stream()
            .collect(Collectors.summingDouble(Employee::getSalary));
    System.out.println(new BigDecimal(sum));

    // Employee information with the largest salary
    Optional<Employee> max = emps.stream()
            .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    System.out.println(max.get());

    // Get only the minimum wage
    Optional<Double> minSalary = emps.stream()
            .map(Employee::getSalary)
            .collect(Collectors.minBy((s1, s2) -> Double.compare(s1, s2)));
    System.out.println(minSalary);
    
    // Comprehensive statistics of all information about wages
    DoubleSummaryStatistics collect = emps.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(collect);
    System.out.println(collect.getMax());
    System.out.println(collect.getAverage());
    System.out.println(collect.getCount());
    System.out.println(collect.getSum());

}

// Group class collection
@Test
public void test6(){

    //1 group by salary
    Map<Double, List<Employee>> collect = emps.stream()
            .collect(Collectors.groupingBy(Employee::getSalary));   // Return the key whose salary is Map
    System.out.println(collect);

    //2 multi level grouping by working status - age
    Map<Status, Map<String, List<Employee>>> collect1 = emps.stream()
            .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(employee -> {
                if (employee.getAge() < 35) {
                    return "youth";
                } else return "middle age";
            })));
    System.out.println(collect1);

    //3 zoning
    Map<Boolean, List<Employee>> collect2 = emps.stream()
            .collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000));
    System.out.println(collect2);
}

4.4 streaming SQL statement

/**
 * Using stream to write SQL statements
 */
public class StreamAPI_SQL {

    public static void main(String[] args) {
        Employee employee1 = new Employee("zdp1", 20,1001);
        Employee employee2 = new Employee("zdp2", 30,1002);
        Employee employee3 = new Employee("zdp3", 40,1003);
        Employee employee4 = new Employee("zdp4", 50,1004);
        Employee employee5 = new Employee("zdp5", 60,1005);
        Employee employee6 = new Employee("zdp6", 70,1006);
        Employee employee7 = new Employee("zdp7", 80,1007);
        Employee employee8 = new Employee("zdp8", 90,1008);

        // Create Employee list
        List<Employee> list = Arrays.asList(employee1, employee2, employee3, employee4, employee5, employee6, employee7, employee8);

        // stream programming to filter employee information with an age greater than 50 and an even salary
        List<Employee> employees = list.stream()
            	// Use multiple filtering intermediate operations
                .filter((employee -> employee.getAge() >= 50))
                .filter(employee -> employee.getSalary() % 2 == 0)
                .collect(Collectors.toList());      // collect

        Iterator<Employee> iterator = employees.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

Tags: Java Back-end

Posted on Mon, 08 Nov 2021 02:42:11 -0500 by majik92