Java 8 new features learning notes

Keywords: Java Lambda Steam Programming

Java 8 new features learning notes

1 main content

  1. Lambda expression
  2. Functional interface
  3. Method reference and constructor reference
  4. Stream API
  5. Default method and static method in interface
  6. New time date API
  7. Other new features

2 succinct

  1. Faster to modify the algorithm of the underlying Hash list, HashMap, HashSet (add list from the previous array - > array + list / red black tree), speed up the speed except adding, concurrent HashMap (CAS algorithm)
  2. More concise code (Lambda expression)
  3. Powerful stream API (easy to operate on datasets)
  4. Parallel for convenience
  5. Minimize null pointer Optional container class

3 Lambda expression

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

Case study:

  //Original anonymous inner class
  public void test1(){
      //comparator 
        Comparator<Integer> com=new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
    }

    //Using Lambda expressions
    public void test2(){
        //x,y is the parameter parameter passed in, and Integer.compare(x,y) is the content within the method.
        Comparator<Integer> com=(x,y)->Integer.compare(x,y);
    }

As shown above, in the new syntax, the code is more concise, - > is called Lambda operator, which divides Lambda into left and right parts:

Left: all parameters required for Lambda expression are specified

Right: Specifies the body of the Lambda, which is the function to be performed by the Lambda expression

There are three kinds of grammatical forms

  1. Syntax format 1: no parameter, no return value, Lambda body only needs one statement

    Running run = () - > system. Out. Println ("executed statement") (create a thread task)

  2. Syntax format 2: Lambda needs a parameter

    Comsumer com = (args)->System.out.println(args)

    If there is only one parameter bracket, args - > system. Out. Println (args) can be omitted (comsumer is a functional programming interface, consumption mode, receiving a parameter, no return value).

  3. Syntax format 3: Lambda requires two parameters with return value

  4. Syntax format 3: Lambda needs three parameters.

    Binary operation bin = (x, y) - > {return x + y;}; (binary operation function interface, receiving two numbers, returning a number of the same type)

    If there is only one statement in the execution body, you can omit return and braces binaryoperator bin = (x, y) - > x + y

There is no parameter type written in the above parameter brackets, because the compiler can infer the type according to the context and the generic type of the method, which is called "type inference".

4 functional interface

An interface that contains only one abstract method is called a functional interface.

In the above Lambda syntax rules, the expression on the right is the actual operation content, which is the implementation of the interface. However, if there are two abstract classes in the interface, which operation content should belong to? Therefore, Lambda expression must have the support of functional interface.

You can create objects of this interface through Lambda expressions. (if a Lambda expression throws a checked exception, the exception needs to be declared on the abstract method of the target interface.)

We can use @ FunctionalInterface annotation on any functional interface to check whether it is a functional interface. Meanwhile, javadoc will also contain a declaration that this interface is a functional interface.

Pass Lambda expression as a parameter: in order to pass a Lambda expression as a parameter, the parameter type that receives the Lambda expression must be the type of a functional interface compatible with the Lambda expression.
Case: define a functional interface of your own

@FunctionalInterface
public interface MyFun {
    public Integer getValue(Integer num);
}

Case: define a functional interface and pass it as a parameter of the method

//Requirement: operate on a number
    @Test
    public void  test6(){
        //Multiple operations can be performed with only one functional interface
        Integer num=operation(100, (x)->x*x);
        System.out.println(num);

        System.out.println(operation(200, (y)->y+200));
    }

    public Integer operation(Integer num,MyFun mf){
        return mf.getValue(num);
    }

Case: call the Collections.sort() method, compare two employees by custom sorting (first by age, the same age by name), and pass the Lambda as a parameter.

List<Employee> employees=Arrays.asList(
            new Employee("Zhang San",18,9496.2),
            new Employee("Li Si",52,2396.2),
            new Employee("Wang Wu",56,996.2),
            new Employee("Zhao Liu",8,94.2)
    );

    @Test
    public void test1(){
        //Using Lambda, you can easily define comparison rules.
         Collections.sort(employees, (e1,e2)->{
             if(e1.getAge()==e2.getAge()){
                 return e1.getName().compareTo(e2.getName());
             }else{
                 return Integer.compare(e1.getAge(), e2.getAge());
             }
         });
    }

To facilitate our use of Lambda expressions, java provides us with a built-in functional interface.

Four functional interfaces in java

  1. Consumer: consumer interface

    void accept(T t);

  2. Supplier: supply interface

    T get();

  3. Function < T, R >: function interface

    R apply(T t);

  4. Preicate: assertion interface

    boolean test(T t);

Other interfaces:

5 method reference and constructor reference

There are three main syntax formats for method references:

Object:: instance method name

Class:: static method name

Class:: instance method name

Be careful:

1. The parameter list and return value type of the calling method in the Lambda body should be consistent with the function list and the return value type of the abstract method in the functional interface.

2. If the first parameter in the Lambda parameter list is the caller of the instance method, and the second parameter is the parameter of the instance method,

(x, y) - > x.equals (y) can be written as String::equals can use ClassName::method

public class TestMethodRef { 

    //Object:: instance method name
    @Test
    public void test1(){
        PrintStream ps1=System.out;   //Print stream objects
        Consumer<String> con=(x)->ps1.println(x);
        //It can be written as follows
        PrintStream ps=System.out;
        Consumer<String> con1=ps::println;//Equivalent to the above, the println() method of ps object is referenced.
        Consumer<String> con2=System.out::println;
        
    }

    @Test
    public void test2(){
        final Employee emp=new Employee();  //An external object is referenced in an anonymous inner class, which must be final
        Supplier<String> sup=()->emp.getName();//Instead of anonymous inner class (the object referenced after jdk1.8 can be used normally even if it does not explicitly add final, but this is just a syntax sugar, and the bottom layer will automatically add final. If it is changed later, an error will be reported.)
       
        Supplier<Integer> sup2=emp::getAge; // It refers to the method of emp object, implements abstract method and supplies to the outside world.
        Integer num=sup2.get();
    }

    //Class:: static method name
    @Test
    public void test3(){
        Comparator<Integer> com=(x,y)->Integer.compare(x,y);
        Comparator<Integer> com1=Integer::compare;//Reference static method
    }

    //Class:: instance method name
    @Test
    public void test4(){
        BiPredicate<String,String> bp=(x,y)->x.equals(y);
        BiPredicate<String, String> bp2=String::equals; //The first parameter is the caller and the second is the argument
    }

Constructor reference:

Format: ClassName::new

Combined with the functional interface, it is automatically compatible with the methods in the functional interface. The constructor reference can be assigned to the defined method, and the constructor parameter list should be consistent with the parameter list of the abstract method in the interface.

//Constructor reference
    @Test
    public void test5(){
        Supplier<Employee> sup=()->new Employee();
        //Constructor reference mode
        Supplier<Employee> sup2=Employee::new;//Using a parameterless constructor
        Employee emp=sup2.get(); //Reference the Employee constructor for provisioning
        //Constructors with parameters
        Function<Integer,Employee> fun2=(x)->new Employee(x);
        BiFunction<String,Integer,Employee> bf=Employee::new;//In this case, the parameter types in the abstract method will automatically match the corresponding constructors
    }

Array reference:

How to create reference arrays

    //Array reference
    @Test
    public void test6(){
        Function<Integer,String[]> fun=(x)->new String[x];
        String[] strs=fun.apply(10);
        System.out.println(strs.length);

        Function<Integer,String[]> fun2=String[]::new;
        String[] str2=fun2.apply(20);
        System.out.println(str2.length);
    }
}

6 Stream Api

There are two most important changes in Java 8.

The first is Lambda expression.

The other is the Stream API(java.util.stream. *).
Stream is a key abstract concept in Java 8 to deal with collections. 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 Stream API to operate on collection data is similar to database query using SQL. You can also use the Stream API to perform operations in parallel.

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

Three steps of Stream operation

  • Create Stream
    A data source (such as a collection or an array) to obtain a stream
  • Intermediate operation
    An intermediate operation chain to process the data of the data source
  • Termination operation (terminal operation)
    A termination operation, performs an intermediate operation chain, and produces results

Create stream

  1. You can use the stream() or parallelStream() methods provided by the Collection under Collection.

    Default stream < E > stream(): returns a sequential stream
    Default stream < E > parallelstream(): returns a parallel stream

  2. Get the array stream through the static method stream() in the Arrays utility class

    Static < T > stream < T > stream (t [] array): receive an array of any type to return a stream of that type

    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)

  3. Create a Stream by displaying the value through the static method of() in the Stream class. It can receive any number of parameters.

    Public static < T > stream < T > of (t... values): receive any number of values of a type and directly create a flow of that type

  4. Create infinite flow

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

  5. iteration
    public static< T> Stream< T> iterate(final T seed, final UnaryOperator< T> f)
    generate
    public static< T> Stream< T> generate(Supplier< T> s)

Here is the case:

//Create Stream
    @Test
    public void test1(){
        //1. stream() or parallelStream() that can be provided through Collection series Collection
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //2. Get array stream through static method stream() in Arrays
        Employee[] emps=new Employee[10];
        Stream<Employee> stream2=Arrays.stream(emps);

        //3. Through the static method of() in the Stream class
        Stream<String> stream3=Stream.of("aa","bb","cc");

        //4. Create infinite flow
        //iteration
        Stream<Integer> stream4=Stream.iterate(0, (x) -> x+2);
        stream4.limit(10).forEach(System.out::println);

        //generate
        Stream.generate(() -> Math.random())
              .limit(5)
              .forEach(System.out::println);
    }

Intermediate operation

Multiple intermediate operations can be connected to form a pipeline. Unless the pipeline triggers the termination operation, the intermediate operation will not perform any processing! When the operation is terminated, it is processed once, which is called "lazy evaluation".

  1. Filtering and slicing

 //Intermediate operation

    List<Employee> employees=Arrays.asList(
            new Employee("Zhang San",18,9999.99),
            new Employee("Li Si",58,5555.55),
            new Employee("Wang Wu",26,3333.33),
            new Employee("Zhao Liu",36,6666.66),
            new Employee("Pseudo-ginseng",12,8888.88),
            new Employee("Pseudo-ginseng",12,8888.88)
            );

    //When the Steam object performs the intermediate operation, it will return the Steam object to implement the chain call.
 //Internal iteration: the iteration operation is completed by the Stream API
    @Test
    public void test1(){
        //Intermediate operation: no operation will be performed (no termination operation)
        //Filtering operation. The filtering rule is defined by the incoming assertion functional interface (in this case, the one over 35 years old)
        Stream<Employee> stream=employees.stream()
                                .filter((e) -> e.getAge()>35 );
        //Terminate operation: perform all contents at once, i.e. lazy evaluation (the above intermediate operation will be performed only when executing this code)
        stream.forEach(System.out::println);
    }

    //External iteration
    @Test
    public void test2(){
        Iterator<Employee> it=employees.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }

    @Test
 //limit the intermediate operation will automatically cut off the flow iteration after finding the specified number of entries
    public void test3(){//It is found that "short circuit" is output only twice, indicating that as long as two qualified ones are found, the iteration will not be continued.
        employees.stream()
                 .filter((e)->{
                     System.out.println("Short circuit!");
                     return e.getSalary()>5000;
                 })
                 .limit(2)
                 .forEach(System.out::println);
    }

    @Test
    public void test4(){
        employees.stream()
                 .filter((e)->e.getSalary()>5000)
                 .skip(2)//Skip the first two
                 .distinct()//De duplication, note: employees need to override hashCode and equals methods
                 .forEach(System.out::println);
    }
  1. mapping

 @Test
    public void test5(){
        List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
        //Will act on each of these elements
        list.stream()
             .map((str)->str.toUpperCase())
             .forEach(System.out::println);

        employees.stream()
                 .map(Employee::getName)
                 .forEach(System.out::println);

     //Map each of these elements to a stream (this case is the stream that turns each string into a character set)
        Stream<Stream<Character>> stream=list.stream()
                                             .map(TestStreamAPI2::filterChatacter);
        //Traverse the main stream, take out each stream, and then traverse each stream to get each character.
        stream.forEach((sm)->{
            sm.forEach(System.out::println);
        });

        System.out.println("------------------------");
     //After each of these elements is turned into a flow, and these flows are merged into a flow, and all the elements are together.
        Stream<Character> sm=list.stream()
                                 .flatMap(TestStreamAPI2::filterChatacter);
        sm.forEach(System.out::println);
    }

    public static Stream<Character> filterChatacter(String str){
        List<Character> list=new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

    @Test
    public void test6(){
        //The relationship between map and flatMap is similar to add(Object) and addAll(Collection coll)
        List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
        List list2=new ArrayList<>();
        list2.add(11);
        list2.add(22);
        list2.addAll(list);
    }
  1. sort


```java
@Test
public void test7(){
//Default natural ordering
List list=Arrays.asList("ccc","bbb","aaa");
list.stream()
.sorted()
.forEach(System.out::println);

          System.out.println("------------------------");
        //By custom collation
          employees.stream()
                   .sorted((e1,e2)->{
                       if(e1.getAge().equals(e2.getAge())){
                           return e1.getName().compareTo(e2.getName());
                       }else{
                           return e1.getAge().compareTo(e2.getAge());
                       }
                   }).forEach(System.out::println); 
      }
  ```

Termination operation

Terminal operations generate results from the flow's pipeline. The result can be any value that is not a stream, such as List, Integer, or even void

  1. Find and match


List<Employee> employees=Arrays.asList(
            new Employee("Zhang San",18,9999.99,Status.FREE),
            new Employee("Li Si",58,5555.55,Status.BUSY),
            new Employee("Wang Wu",26,3333.33,Status.VOCATION),
            new Employee("Zhao Liu",36,6666.66,Status.FREE),
            new Employee("Pseudo-ginseng",12,8888.88,Status.BUSY)
            );
    /*
     * Find and match
     * 
     */

    @Test
    public void test1(){
        boolean b1=employees.stream()//allMatch - check whether all elements are matched (with BUSY status)
                            .allMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b1);//false

        boolean b2=employees.stream()//anyMatch - check if at least one element is matched (at least one is BUSY)
                            .anyMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b2);//true

        boolean b3=employees.stream()//noneMatch - check if not all elements are matched (none of them)
                            .noneMatch((e)->e.getStatus().equals(Status.BUSY));
        System.out.println(b3);//false

        //findFirst - returns the first element / / Optional is a container class in Java 8 to avoid null pointer exceptions
        Optional<Employee> op=employees.stream()
                 .sorted((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))
                 .findFirst();
        System.out.println(op.get());//Employee [name = Wang Wu, age = 26, salary = 3333.33, status = voice]

        Optional<Employee> op2=employees.parallelStream()//findAny - returns any element in the current flow
                                        .filter((e)->e.getStatus().equals(Status.FREE))
                                        .findAny();
        System.out.println(op2.get());//Employee [name = Zhao Liu, age=36, salary=6666.66, Status=FREE]

        Long count=employees.stream()//count - returns the total number of elements in the stream
                            .count();
        System.out.println(count);//5

        Optional<Employee> op3=employees.stream()//max - returns the maximum value in the flow (the comparison rule can be defined and placed upside down)
                                        .max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op3.get());//Employee [name = Zhang San, age=18, salary=9999.99, Status=FREE]

        Optional<Double> op4=employees.stream()//min - minimum value in return flow
                                      .map(Employee::getSalary)
                                      .min(Double::compare);
        System.out.println(op4.get());//3333.33
    }
  1. Statute

can  
/*
     * Reduction (can be combined with map)
     * reduce(T identity,BinaryOperator b) / reduce(BinaryOperator b)-You can combine 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);
        Integer sum=list.stream()//reduce(T identity,BinaryOperator b)
                        .reduce(0, (x,y)->x+y);//0 is the starting value
        System.out.println(sum);

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

        Optional<Double> op=employees.stream()//reduce(BinaryOperator b) / / no starting value, map may return empty, so the Optional type is returned.
                                     .map(Employee::getSalary)
                                     .reduce(Double::sum);
        System.out.println(op.get());
    }
  1. collect

    The implementation of the methods in the Collector interface determines how collection operations (such as List, Set, Map) are performed on the stream. However, the Collectors utility class provides many static methods, which can easily create common Collector instances. The specific methods and instances are as follows:



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

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

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

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

        HashSet<String> hs=employees.stream()
                                    .map(Employee::getName)
                                    .collect(Collectors.toCollection(HashSet::new));
        hs.forEach(System.out::println);

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

        //The sum
        Long count=employees.stream()
                            .collect(Collectors.counting());
        System.out.println(count);

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

        //The sum
        Double sum=employees.stream()
                            .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        //Maximum value
        Optional<Employee> max=employees.stream()
                                        .collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());

        //minimum value
        Optional<Double> min=employees.stream()
                                      .map(Employee::getSalary)
                                      .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());

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

        //Grouping
        Map<Status,List<Employee>> map=employees.stream()
                                                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);//FREE=[Employee [name = Zhang San, age=18, salary=9999.99, Status=FREE], Employee [name = Zhao Liu, age = 36, salary = 6666.66, status = free]], voice = [employee [name = Wang Wu, age = 26, salary = 3333.33, status = voice]], busy = [employee [name = Li Si, age=58, salary=5555.55, Status=BUSY], Employee [name = Tian Qi, age = 12 = 12, age = 12 = field 7, age = 12, age = 12, age = 12, salary = 3333.33, status = voice], busy = [employee [name = employee [name = Li Si, name = Li Si, age = 58, salary = 5555.55, status = 5555, busy], employee [name = Tian Qi, age = 12 = 12 12, age = 12 = field qi, salary = 8888.88, status = busy]]}

        //Multilevel grouping
        Map<Status,Map<String,List<Employee>>> map2=employees.stream()
                                                            .collect( Collectors.groupingBy( Employee::getStatus,Collectors.groupingBy((e)->{
                                                                if(e.getAge()<=35){
                                                                    return "Youth";
                                                                }else if(e.getAge()<=50){
                                                                    return "middle age";
                                                                }else{
                                                                    return "old age";
                                                                }
                                                            }) ) );
        System.out.println(map2);//FREE = {FREE = {youth = [Employee [name = Zhang San, age=18, salary=9999.99, Status=FREE]] {youth = [Employee [name = Zhao Liu, age = 36, salary = 6666.66, status = FREE]]}, voice = {youth = [Employee [name = Wang Wu, age = 26, salary = 3333.33, status = voice]]}, business = {youth = [Employee [name = Tian Qi, age = 12, salary = 8888.88.88, 8888.88, 88 = 8888.88]]}, voice = {youth = {[Employee [name = Employee [name = Tian Qi, age = 12, salary = 8888.88, 8888.88.88, 8888.88 = 8888.88.88, number = 8888.88.88].]]]}, status = busy], senior = [Employee [name = Li Si, age = 58, s Alary = 5555.55, status = busy]]}}}

        //partition
        Map<Boolean,List<Employee>> map3=employees.stream()
                                                 .collect(Collectors.partitioningBy((e)->e.getSalary()>8000));
        System.out.println(map3);//false=[Employee [name = Li Si, age=58, salary=5555.55, Status=BUSY], Employee [name = Wang Wu, age = 26, salary = 3333.33, status = voice], employee [name = Zhao Liu, age = 36, salary = 6666.66, status = free]] true = [employee [name = Zhang San, age=18, salary=9999.99, Status=FREE], Employee [name = Tian Qi, age = 12, salary = 8888.8888.8888.8888.8.8888.88, status = free], true = [employee [name = Zhang San, age = 18, salary = 99999.99, status = free], employee [name = Tian Qi, age = 12, salary = 8888.8888.8888.8888.8.8.8888.8.8.8888.8.12, salary = 88, status = busy]]}

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

        DoubleSummaryStatistics dss=employees.stream()
                                             .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
        System.out.println(dss.getMax());

        System.out.println("--------------------------------");
        String strr=employees.stream()
                             .map(Employee::getName)
                             .collect(Collectors.joining(","));
        System.out.println(strr);//Zhang Sanli Siwang Wuzhao liutianqi
     }

Posted by joshblue on Wed, 16 Oct 2019 04:44:42 -0700