Java: new features in Java 8

1. Lambda expression

1.1 benefits of lambda expressions

Lambda is an anonymous function. We can understand lambda expression as a piece of code that can be passed (pass the code like data)

Using it, you can write more concise and flexible code. As a more compact code style, the language expression ability of Java has been improved

The essence of Lambda expression is: as an instance of functional interface

1.2 example of lambda expression

//The imported packages are:; import org.junit.Test;import java.util.Comparator;

public class LambdaTest1 {
    @Test
    public void test1(){
        Runnable r1 = new Runnable(){
            @Override
            public void run() {
                System.out.println("I Love Beijing Tiananmen ");
            }
        };
        r1.run();

        //Lambad expression
        Runnable r2 = () -> System.out.println("I Love Beijing Tiananmen ");
        r2.run();
    }
    @Test
    public void test2(){
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        int compare1 = com1.compare(12,21);
        System.out.println(compare1);

        //Lambda expression
        Comparator<Integer> com2 = (o1,o2) ->Integer.compare(o1,o2);
        int compare2 = com2.compare(12,21);
        System.out.println(compare2);

        //Method reference
        Comparator<Integer> com3 = Integer :: compare;
        int compare3 = com3.compare(12,21);
        System.out.println(compare3);
    }
}

1.3 use of lambda expressions

For example: (O1, O2) - > integer compare(o1,o2)

Format:

  • ->: Lambda operator or arrow operator
  • ->Left: Lambda formal parameter list (in fact, it is the formal parameter list of abstract methods in the interface)
  • ->Right: Lambda body (actually the method body of the overridden abstract method)

Syntax format:

  1. Syntax format 1: no parameter, no return value

  2. Syntax format 2: Lambda needs a parameter, but there is no return value

  3. Syntax format 3: data types can be omitted because they can be inferred by the compiler, which is called "type inference"

  4. Syntax format 4: if Lambda only needs one parameter, the parentheses of the parameter can be omitted

  5. Syntax format 5: Lambda requires two or more parameters, multiple execution statements, and can have return values

  6. Syntax format 6: when there is only one statement in Lambda body, return and braces, if any, can be omitted

In general, the syntax format is:

  • ->Left: the parameter type of lambda parameter list can be omitted (type inference). If the parameter list has only one parameter, its pair () can also be omitted
  • ->Right: the lambda body should use a pair of {} packages. If the lambda body has only one execution statement (possibly a return statement), omit this pair of {} and return keywords
//The imported packages are: import org junit. Test; import java. util. Comparator; import java. util. function. Consumer;

public class LambdaTest2 {
    @Test
    public void test1(){
        //Syntax format 1: no parameter, no return value
        Runnable r1 = new Runnable(){
            @Override
            public void run() {
                System.out.println("I Love Beijing Tiananmen ");
            }
        };
        r1.run();

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

        //Lambad expression
        Runnable r2 = () -> {
            System.out.println("I Love Beijing Tiananmen ");
        };
        r2.run();
    }
    @Test
    public void test2(){
        //Syntax format 2: Lambda needs a parameter, but there is no return value
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("Today is a good day");

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

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("Today is a good day");
    }
    @Test
    public void test3(){
        //Syntax format 3: data types can be omitted because they can be inferred by the compiler, which is called "type inference"
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("Today is a good day");

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

        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("Today is a good day");
    }
    @Test
    public void test4(){
        //Syntax format 4: if Lambda only needs one parameter, the parentheses of the parameter can be omitted
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };
        con1.accept("Today is a good day");

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

        Consumer<String> con2 = s -> {
            System.out.println(s);
        };
        con2.accept("Today is a good day");
    }
    @Test
    public void test5(){
        //Syntax format 5: Lambda requires two or more parameters, multiple execution statements, and can have return values
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };

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

        Comparator<Integer> com2 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
    }
    @Test
    public void test6(){
        //Syntax format 6: when there is only one statement in Lambda body, return and braces, if any, can be omitted
        Comparator<Integer> com1 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };

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

        Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
    }
}

2. Functional interface

  • An interface that contains only one abstract method is called a functional interface
  • You can create objects of this interface through Lambda expressions
  • We can use the @ FunctionalInterface annotation on an interface to check whether it is a functional interface
  • Those previously expressed by anonymous implementation classes can now be written in Lambda expressions
  • In Java util. The function package defines the rich functional interfaces of Java 8

Reference builder and method

3.1 method reference

  1. Usage scenario: when the operation to be passed to the Lambda body already has an implemented method, you can use the method reference

  2. Essence of method reference: its essence is Lambda expression, and Lambda expression is an instance of functional interface, so method reference is also an instance of functional interface

  3. Use format: class (object):: method name

  4. Specific usage:

    • Object:: non static method (instance method)

    • Class:: static method

    • Class:: non static method

  5. Usage requirements: the formal parameter list and return value type of the abstract method in the interface are required to be the same as those of the method referenced by the method, but they can be different when the class calls a non static method

3.2 constructor reference and array reference

Constructor reference

Similar to method reference, the formal parameter list of abstract method of functional interface is consistent with that of constructor. The return value type of the abstract method is the type of the class to which the constructor belongs

Array reference

The array can be regarded as a special class, and the writing method is consistent with the constructor reference

4. Powerful Stream API

4.1 Stream API overview

What is the Stream API

  • The stream API (Java. Util. Stream) introduces a true functional programming style into Java
  • Stream is the key abstract concept of dealing with collections in Java 8. It can specify the operations you want to perform on collections, and can perform very complex operations such as finding, filtering and mapping data
  • Using the Stream API to operate on the collection data is similar to using SQL to execute database queries
  • The difference between Stream and Collection: Collection is a static memory data structure, while Stream is about calculation. The former is mainly memory oriented and stored in memory, while the latter is mainly CPU oriented and realizes computing through CPU
  • Set is about data, Stream is about calculation

Operation steps of Stream

  1. Create Stream: a data source (such as collection and array) to obtain a Stream
  2. Intermediate operation: an intermediate operation chain that processes the data of the data source
  3. Termination operation (terminal operation): once the termination operation is executed, the intermediate operation chain is executed and the results are generated. After that, it will not be used again

matters needing attention

  • Stream itself does not store elements
  • Stream does not change the source object. Instead, they return a new stream that holds the result
  • Stream operations are deferred. This means they wait until they need results

4.2 instantiation of stream

There are four ways to instantiate a Stream:

  1. Method 1 of creating Stream: through collection
  2. Method 2 of creating Stream: through array
  3. Three ways to create a Stream: through the of() of the Stream
  4. Four ways to create a Stream: create an infinite Stream (rarely used)
//The imported packages are: import org junit. Test; import java. util. Arrays; import java. util. List; import java. util. stream. IntStream; import java. util. stream. Stream;

public class StreamTest {
    //Method 1 of creating Stream: through collection
    @Test
    public void test1(){
        //Create a collection first
        List<Employee> employees = EmployeeData.getEmployees();

        //Default stream < E > stream(): returns a sequential stream
        Stream<Employee> stream = employees.stream();

        //Default stream < E > parallelstream(): returns a parallel stream
        Stream<Employee> parallelStream = employees.parallelStream();
    }

    //Method 2 of creating Stream: through array
    @Test
    public void test2(){
        int[] arr = new int[]{1,2,3,4,5,6};
        //Call static < T > stream < T > stream (t [] Array) of Array class: return a stream
        IntStream stream = Arrays.stream(arr);

        Employee e1 = new Employee(1001,"Tom");
        Employee e2 = new Employee(1002,"Jerry");
        Employee[] arr1 = new Employee[]{e1,e2};
        //Common arrays are OK, and custom array arr1 is also OK
        Stream<Employee> stream1 = Arrays.stream(arr1);
    }

    //Three ways to create a Stream: through the of() of the Stream
    @Test
    public void test3(){
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
    }
}

4.3 intermediate operation of stream

  1. Screening and slicing

    //Screening and slicing
        @Test
        public void test1(){
            List<Employee> list = EmployeeData.getEmployees();
            //Filter (predict P): receive Lambda and exclude some elements from the stream
            Stream<Employee> stream = list.stream();
            //Query the information of employees whose salary is greater than 7000 in the employee table
            stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
    
            //limit(n): truncate the stream so that its elements do not exceed the given number
            list.stream().limit(3).forEach(System.out::println);
    
            //skip(n): 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 will be returned
            list.stream().skip(3).forEach(System.out::println);
    
            //distinct(): filter and remove duplicate elements through hashCode() and equals() of the elements generated by the stream
            list.stream().distinct().forEach(System.out::println);
        }
    
  2. mapping

    //mapping
        @Test
        public void test2(){
            //Receive a function as an argument, which is applied to each element and mapped to a new element
            List<String> list = Arrays.asList("aa","bb","cc","dd");
            list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
        }
    
  3. sort

    //sort
        @Test
        public void test3(){
            //sorted(): natural sorting
            List<Integer> list = Arrays.asList(12,45,-8,2,56,34);
            list.stream().sorted().forEach(System.out::println);
            //The condition for using natural sorting is that the specified generic type implements the Comparable interface
    
            //sorted(Comparator com): customized sorting
            List<Employee> employees = EmployeeData.getEmployees();
            employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()))
                    .forEach(System.out::println);
        }
    

4.4 termination of stream

  1. Match and find

    //Match and find
        @Test
        public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();
            //Allmatch (predict P): check whether all elements are matched: whether all employees are older than 18
            boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
            System.out.println(allMatch);
    
            //Anymatch (predict P): check whether at least one element is matched: whether there is an employee whose salary is greater than 1000
            boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 1000);
            System.out.println(anyMatch);
    
            //Nonematch (predict P): check whether all elements are not matched: whether there is an employee surname "Lei"
            boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("thunder"));
            System.out.println(noneMatch);
    
            //First element: first find()
            Optional<Employee> employee = employees.stream().findFirst();
            System.out.println(employee);
    
            //findAny(): returns any element in the current stream
            Optional<Employee> employee1 = employees.stream().findAny();
            System.out.println(employee1);
    
            //count(): returns the total number of elements in the stream
            long count = employees.stream().count();
            System.out.println(count);
    
            //max(Comparator c): returns the maximum value in the flow: returns the highest salary
            Optional<Double> maxSalary = employees.stream().map(e -> e.getSalary()).max(Double::compare);
            System.out.println(maxSalary);
    
            //min(Comparator c): returns the minimum value in the flow: returns the minimum wage
            Optional<Employee> minSalary = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
            System.out.println(minSalary);
    
            //forEach(Consumer c): internal iteration
            // Using the Collection interface requires users to iterate, which is called external iteration
            // Instead, the Stream API uses internal iterations -- it helps you do the iterations
            employees.stream().forEach(System.out::println);
        }
    
  2. reduction

    //Statute
        @Test
        public void test2(){
            //Reduce (T iden, binary operator b) can combine the elements in the stream repeatedly to get a value. Return T
            //Exercise: calculate the sum of natural numbers 1-10
            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            Integer sum = list.stream().reduce(0, Integer::sum);
            System.out.println(sum);
    
            //Reduce (binary operator b) can combine the elements in the stream repeatedly to get a value. Return to optional < T >
            //Exercise: calculate the total salary of all employees in the company
            List<Employee> employees = EmployeeData.getEmployees();
            Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
            Optional<Double> sumMoney = salaryStream.reduce((d1, d2) -> d1 + d2);
            System.out.println(sumMoney);
        }
    
  3. collect

    //collect
        @Test
     public void test3(){
            //collect(Collector c) converts a Stream to another form. Receive the implementation of a collector interface, which is used to summarize the elements in the Stream
            //Exercise: find employees whose salary is greater than 6000, and the result is returned as a List or Set
    
            //The result is returned as a List
            List<Employee> employees = EmployeeData.getEmployees();
            List<Employee> list = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
            list.forEach(System.out::println);
    
            //The result is returned as a Set
            Set<Employee> set = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
            list.forEach(System.out::println);
        }
    

5. Optional class

The optional < T > class (java.util.Optional) is a container class that can store the value of type T, representing the existence of this value. Or just save null, indicating that the value does not exist.

Originally, null was used to indicate that a value does not exist. Now Optional can better express this concept. And you can avoid null pointer exceptions

Optional provides many useful methods so that we don't need to explicitly detect null values:

  1. Method to create an Optional class object:
    • Optional. Of (t): create an optional instance. t must be non empty
    • Optional.empty(): create an empty optional instance
    • Optional. Ofnullable (T): t can be null
  2. Determine whether the Optional container contains objects:
    • boolean isPresent(): judge whether objects are included
  3. Get the object of the Optional container:
    • If the object contains an exception value, call get(), otherwise: t returns the exception value
    • T orElse(T other) * *: * * if there is a value, it will be returned; otherwise, the specified other object will be returned

Give an example of the Optional class:

Tags: Java stream

Posted by simonb on Tue, 24 May 2022 05:54:51 +0300