Java functional design

Implementation method:

  • @ FunctionalInterface interface
  • Lambda syntax
  • Method references
  • Interface default method implementation

Lambda expressions

Lambda expressions are shorthand for anonymous inner classes, similar to syntactic sugar for anonymous inner classes; But it’s different from anonymous inner classes (more on that later).

Anonymous inner class features:

  • Polymorphic based (mostly interface based programming)
  • The implementation class does not need a name
  • Multiple abstract methods are allowed

Lambda’s syntax is concise and unfettered by the complexities of object orientation. Features:

  1. To use Lambda, you must have an interface with one and only one abstract method in the interface. Lambda can only be used if the abstract methods in the interface exist and are unique, but the default interface methods and the exposed methods in declarations that override Object are excluded.
  2. Lambda must be used with context inference. That is, the parameter or local variable type of a method must be the interface type corresponding to Lambda in order to use Lambda as an instance of that interface.

Note: An interface with only one abstract method is called a functional interface.

The standard format consists of three parts:

  • Some of the parameters
  • An arrow
  • A code format:
(Parameter list) ->{code for some important methods}; (): the parameter list of the abstract method in the interface, empty if there are no parameters; Write arguments if you have them, and separate multiple arguments with commas. -> : Pass: passes parameters to the method body {} {}: overrides the abstract method body of the interfaceCopy the code

The left side of the arrow operator corresponds to the list of arguments in the interface (the argument list of a lambda expression), and the right side of the arrow is the implementation of the abstract method (what a lambda expression needs to do).

Lambda optimization: omit anything that can be deduced from the context:

(argument list) : The data type of the argument list in parentheses, which can be omitted. (Argument list) : If there is only one argument in parentheses, both the type and () can be omitted. {some code} : If the code in {} is only one line, the ({}, return, semicolon) can be omitted.Copy the code

Note: to omit {},return, the semicolon must be omitted together

public class MyLambda {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println(Thread.currentThread().getName()+"New thread created");
            }
        }).start();

        / / using the Lambda
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"New thread created");
        }).start();

        Lambda / / optimization
        new Thread(()->System.out.println(Thread.currentThread().getName()+"New thread created")).start(); }}Copy the code

1.1 No Parameter, no return

()->System.out.println("hello lambda")

Cook:

public interface Cook {
    public abstract void makeFood(a);
}
Copy the code

Demo01Cook:

public class Demo01Cook {

    public static void main(String[] args) {
        invokeCook(new Cook() {
            public void makeFood(a) {
                System.out.println("Cooking..."); }});/ / using the Lambda
        invokeCook(()->{
            System.out.println("Cooking...");
        });

        Lambda / / optimization
        invokeCook(()-> System.out.println("Cooking..."));
    }

    public static void invokeCook(Cook cook){ cook.makeFood(); }}Copy the code

1.2 No return is returned if there are parameters

x->System.out.println("hello lambda")

import java.util.function.Consumer;

/** * Created by hongcaixia on 2019/10/31. */
public class Demo2 {

    public static void main(String[] args) {
        Consumer<String> consumer = x-> System.out.println(x);
        consumer.accept("No return with parameters"); }}Copy the code

1.3 If there are parameters, there is a return

(Person p1,Person p2)->{ return p1.getAge()-p2.getAge(); }

package com.hcx.lambda;

import java.util.Arrays;
import java.util.Comparator;

/** * Created by hongcaixia on 2019/10/26. */
public class MyArrays {

    public static void main(String[] args) {
        Person[] arr = {new Person(Eason Chan.40),
                        new Person("Chung Hon-leung".39),
                        new Person(Miriam Yeung.38)};

        // Sort by age
        Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                returno1.getAge()-o2.getAge(); }});/ / using the lambda
        Arrays.sort(arr,(Person p1,Person p2)->{
            return p1.getAge()-p2.getAge();
        });

        Lambda / / optimization
        Arrays.sort(arr,(p1,p2)->p1.getAge()-p2.getAge());

        Arrays.sort(arr,Comparator.comparing(Person::getAge));

        for(Person p:arr){ System.out.println(p); }}}Copy the code

2. Functional interfaces

2.1 concept

Functional interface: An interface with and only one abstract method.

Functional interfaces, that is, interfaces suitable for functional programming scenarios. And functional programming in Java is Lambda, so functional interfaces are interfaces that can be applied to Lambda. Lambdas in Java can only be derived smoothly by ensuring that there is one and only one abstract method in the interface.

2.2 format

The modifierinterfaceThe name of the interface{
    public abstractReturn value type Method name (optional parameter information);// Other non-abstract method content
}
Copy the code

Public abstract of abstract methods can be omitted:

public interface MyFunctionalInterface {   
    void myMethod(a);    
}
Copy the code

2.3 @ FunctionalInterface annotation

Similar to the @Override annotation, a new annotation was introduced in Java 8 specifically for functional interfaces: @FunctionalInterface. Informational annotation types for functional interface type declarations, instances of which are created by Lambda expressions, method references, or constructor references. A FunctionalInterface can have only one abstract method, excluding the interface’s default methods and the declared public methods that override Object:

Source code comments: This annotation is the basis for defining a lambda expression, whether or not a functional interface can be annotated. Functional interfaces must have a precise abstract method, but exclude the following two: ①java8’s default, they are not abstract methods. (2) If the interface declares an abstract method that overrides a method of any object, this method is excluded.

@FunctionalInterface
public interface MyFunctionInterface {
    // The only abstract method
    void method(a);
    // Exclude the default modifier
    default void method1(a){}// exclude methods under Ojbect
    int hashCode(a);
}
Copy the code
@FunctionalInterface
public interface MyFunctionalInterface {
	void myMethod(a);    
}
Copy the code

Note: @funcationLInterface cannot be annotated on annotations, classes, or enumerations. Once the annotation is used to define the interface, the compiler forces a check to see if the interface does have one and only one abstract method, or an error will be reported. Note that even if this annotation is not used, this is still a functional interface and works the same as long as it satisfies the definition of a functional interface.

MyFunctionalInterface:

@FunctionalInterface
public interface MyFunctionalInterface {
    // Define an abstract method
    public abstract void method(a);
}
Copy the code

MyFunctionalInterfaceImpl:

public class MyFunctionalInterfaceImpl implements MyFunctionalInterface{
    @Override
    public void method(a) {}}Copy the code

Demo:

/* Use of functional interfaces: generally used as method parameters and return value types */
public class Demo {
    // Define a method using the functional interface MyFunctionalInterface
    public static void show(MyFunctionalInterface myInter){
        myInter.method();
    }

    public static void main(String[] args) {
        // Call the show method, whose argument is an interface, so you can pass an implementation-class object for that interface
        show(new MyFunctionalInterfaceImpl());

        // Call the show method, whose argument is an interface, so we can pass the anonymous inner class of the interface
        show(new MyFunctionalInterface() {
            @Override
            public void method(a) {
                System.out.println("Overriding abstract methods in interfaces using anonymous inner classes"); }});// Call the show method, whose argument is a functional interface, so we can Lambda expressions
        show(()->{
            System.out.println("Override abstract methods in interfaces using Lambda expressions");
        });

        // Simplify Lambda expressions
        show(()-> System.out.println("Override abstract methods in interfaces using Lambda expressions")); }}Copy the code

Case study: Analysis: The difference between functional interfaces and ordinary methods

import java.util.function.Supplier;

/** * Created by hongcaixia on 2019/11/3. */
public class MyTest {
    public static void main(String[] args) {
        print1("hello,world");
        print2(()->"hello world");
    }

    public static void print1(String message){
        System.out.println(message);
    }

    public static void print2(Supplier<String> message){ System.out.println(message.get()); }}Copy the code

The above code yields the same result, but using a functional interface is equivalent to lazily loading the data. With a functional interface, the data is not fully determined until it is actually called, similar to the push model.

Demo01Logger:

public class Demo01Logger {
    // Define a method to display log information based on the log level
    public static void showLog(int level, String message){
        // Determine the log level. If the log level is 1, output the log information
        if(level==1){ System.out.println(message); }}public static void main(String[] args) {
        // Define three log messages
        String msg1 = "Hello";
        String msg2 = "World";
        String msg3 = "Java";

        // Call the showLog method to pass the log level and log information
        showLog(2,msg1+msg2+msg3); }}Copy the code

Demo02Lambda:

/* Use Lambda optimizationlog case Lambda features: Lazy loading of Lambda to use the prerequisite, there must be a functional interface */
public class Demo02Lambda {
    // Define a method to display the log, with parameters passing the log level and the MessageBuilder interface
    public static void showLog(int level, MessageBuilder mb){
        // Determine the level of the log. If it is level 1, call the builderMessage method in the MessageBuilder interface
        if(level==1){ System.out.println(mb.builderMessage()); }}public static void main(String[] args) {
        // Define three log messages
        String msg1 = "Hello";
        String msg2 = "World";
        String msg3 = "Java";

        // Call the showLog method with the MessageBuilder argument, which is a functional interface, so you can pass Lambda expressions
        /*showLog(2,()->{// return msg1+msg2+msg3; }); * /

        showLog(1,()->{
            System.out.println("No execution until conditions are met.");
            // Returns a concatenated string
            returnmsg1+msg2+msg3; }); }}Copy the code

MessageBuilder:

@FunctionalInterface
public interface MessageBuilder {
    // Define an abstract method for concatenating messages that returns the concatenated messages
    public abstract String builderMessage(a);
}
Copy the code

Analysis: Lambda expression is used as the parameter to pass, just pass the parameter to the showLog method, only meet the conditions, log level is 1 will call the method of builderMessage interface MessageBuilder, will be string splicetogether; If the conditions are not met and the log level is not level 1, then the method builderMessage in the MessageBuilder interface is not executed, so the code for concatenating strings is not executed, so there is no wasted performance

2.4 Use functional interfaces as method parameters

Demo01Runnable:

public class Demo01Runnable {
    // Define a method, startThread, whose arguments use the functional interface Runnable
    public static void startThread(Runnable run){
        // Enable multithreading
        new Thread(run).start();
    }

    public static void main(String[] args) {
        // Call the startThread method, whose argument is an interface, so we can pass the anonymous inner class of that interface
        startThread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println(Thread.currentThread().getName()+"-- >"+"Thread started"); }});// Call the startThread method, whose argument is a functional interface, so you can pass Lambda expressions
        startThread(()->{
            System.out.println(Thread.currentThread().getName()+"-- >"+"Thread started");
        });

        // Optimize Lambda expressions
        startThread(()->System.out.println(Thread.currentThread().getName()+"-- >"+"Thread started")); }}Copy the code

2.5 Use a functional interface as the return value of a method

Demo02Comparator:

import java.util.Arrays;
import java.util.Comparator;

/* If the return type of a method is a functional interface, then a Lambda expression can be returned directly. This method is invoked when a method is needed to get an object of type java.util.Comparator interface as a collator. * /
public class Demo02Comparator {
    // Define a method whose return value type uses a functional interface Comparator
    public static Comparator<String> getComparator(a){
        The return type of the method is an interface, so we can return the anonymous inner class of that interface
        /*return new Comparator
      
       () { @Override public int compare(String o1, Return O2.length ()-o1.length(); }}; * /
      

        // The return type of the method is a functional interface, so we can return a Lambda expression
        /*return (String o1, String o2)->{return o2.length()-o1.length(); }; * /

        // Continue to optimize Lambda expressions
        return (o1, o2)->o2.length()-o1.length();
    }

    public static void main(String[] args) {
        // Create an array of strings
        String[] arr = {"a"."bb"."ccc"."dddd"};
        // Outputs the sorted array
        System.out.println(Arrays.toString(arr));
        // Call the sort method of Arrays to sort the string Arrays
        Arrays.sort(arr,getComparator());
        // Outputs the sorted arraySystem.out.println(Arrays.toString(arr)); }}Copy the code

Each time you declare an interface, write an abstract method, and then use that interface as an argument to implement lambda… Don’t need it! This new feature is designed to keep things simple, so Java already has a bunch of functional interfaces built in.

Let’s start with an overview of some commonly used tables:

Functional interface The parameter types The return type use
: Supplier GongGeiXing There is no T Return an object of type T, T get()
Consumer consumption T void Void accept(T T); void accept(T T);
Predicate to break stereotypes T boolean Determine whether an object of type T satisfies a constraint, return a Boolean, method: Boolean test(T T)
The Function type < T, R > Function T R Apply an operation on an object of type T and return an object of type R using R apply(T, T).

2.6 Common Functional Interfaces

(1) Supplier interface

Features: not only leave, in a method/structure parameters, the return value of Java. Util. Function. : Supplier interface contains only a no arguments method: T get (). Gets object data of the type specified by a generic parameter.

The Supplier interface is called a production interface. What type of data will be produced by the get method in the interface, given the generic type of the interface

import java.util.function.Supplier;

/** * Created by hongcaixia on 2019/10/29. */
public class MySupplier {

    public static String getString(Supplier<String> supplier){
        return supplier.get();
    }

    public static void main(String[] args) {
        getString(new Supplier<String>() {
            @Override
            public String get(a) {
                return null; }}); String s = getString(()->"Eason"); System.out.println(s); }}Copy the code

GetMax:

import java.util.function.Supplier;

/** * Created by hongcaixia on 2019/10/29. */
public class GetMax {

    public static int getMaxNum(Supplier<Integer> supplier){
        return supplier.get();
    }

    public static void main(String[] args) {
        int[] arr = {-1.0.1.2.3};
        int maxValue = getMaxNum(()->{
            int max = arr[0];
            for(int i=0; i<arr.length; i++){if(arr[i]>max){ max = arr[i]; }}return max;
        });
        System.out.println("The maximum value of an array element is:"+maxValue); }}Copy the code
② Consumption type: Consumer interface

Features: not only, as a method/Java structure parameters. The util. The function. The Consumer interface is just the opposite with: Supplier interface, it is not a production data, but the consumption of a data, the data type is determined by the generic. The Consumer interface contains the abstract method void Accept (T T), which means consuming data of a specified generic type.

The Consumer interface is a Consumer interface. The accept method can be used to consume whatever type of data a generic type executes

import java.util.function.Consumer;

/** * Created by hongcaixia on 2019/10/29. */
public class MyConsumer {

    public static void method(String name, Consumer<String> consumer){
        consumer.accept(name);
    }

    public static void main(String[] args) {
        method("Wow!".new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("S is."+s); }}); method("Wow!",(name)->{
            String s = new StringBuffer(name).reverse().toString();
            System.out.println("S is."+s); }); }}Copy the code

AndThen: The default method for the Consumer interface. The purpose of andThen is to combine two Consumer interfaces to consume data

import java.util.function.Consumer;

/** * Created by hongcaixia on 2019/10/30. */
public class AndThen {

    public static void method(String s, Consumer<String> consumer1,Consumer<String> consumer2){
// consumer1.accept(s);
// consumer2.accept(s);
        // Use the andThen method to connect the two Consumer interfaces to consume data
        Con1 consumes data before con2 consumes data
        consumer1.andThen(consumer2).accept(s);
    }

    public static void main(String[] args) {
        method("Hello",
                (t)-> System.out.println(t.toUpperCase()), // Consume: convert the string to uppercase output
                (t)-> System.out.println(t.toLowerCase()));// Consume: Convert string to lowercase output


        method("Hello".new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s.toUpperCase());
            }},new Consumer<String>() {
                @Override
                public void accept(String s1) { System.out.println(s1.toUpperCase()); }}); }}Copy the code

The format is name: XX. Gender: XX.” The format of the information printing request takes the action of printing the name as the Lambda instance of the first Consumer interface and the action of printing the gender as the Lambda instance of the second Consumer interface, “stitching” the two Consumer interfaces together in sequence.

public class DemoTest {
    // Define a method that takes a String array and two Consumer interfaces. Generics use String
    public static void printInfo(String[] arr, Consumer<String> con1,Consumer<String> con2){
        // Iterate over an array of strings
        for (String message : arr) {
            // Connect the two Consumer interfaces using the andThen method to consume stringscon1.andThen(con2).accept(message); }}public static void main(String[] args) {
        // Define an array of type string
        String[] arr = { "Eason Chan, male"."Chung Hon-leung, male".Hu Ge, male };

        // Call the printInfo method, passing in an array of strings and two Lambda expressions
        printInfo(arr,(message)->{
            // Consume: Cut the message, get the name, output in the specified format
            String name = message.split(",") [0];
            System.out.print("Name:"+name);
        },(message)->{
            // Consumption mode: Cut the message, get the age, output according to the specified format
            String age = message.split(",") [1];
            System.out.println("; Age:"+age+"。"); }); }}Copy the code
③ Predicate type: Predicate interface

Features: a Boolean type judgment, as a method/Java. The structure parameters util. The function. The Predicate interface functions: data of a certain data type judgment, the result returns a Boolean value

The Predicate interface contains an abstract method: Boolean test(T T): a method used to judge data of a specified data type. Result: True if the condition is met, false if the condition is not met

import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/30. */
public class MyPredicate1 {

    public static boolean validateStr(String str, Predicate<String> predicate){
        return predicate.test(str);
    }

    public static void main(String[] args) {
        String str = "abcdef";
        boolean b = validateStr(str,string->str.length()>5);
        System.out.println(b);


        boolean b1 = validateStr(str, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length()>5; }}); System.out.println(b1); }}Copy the code

And method: The Predicate interface has a method called AND, which represents the and relationship, and can also be used to connect two judgments

default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> this.test(t) && other.test(t);
}
Copy the code

MyPredicateAnd :

import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/30. */
public class MyPredicateAnd {

    public static boolean validateStr(String str, Predicate<String> pre1,Predicate<String> pre2){
// return pre1.test(str) && pre2.test(str);
        return pre1.and(pre2).test(str);
    }

    public static void main(String[] args) {
        String s = "abcdef";
        boolean b = validateStr(s,str->str.length()>5,str->str.contains("a")); System.out.println(b); }}Copy the code

Or method: The Predicate interface has a method or, which represents or relations. It can also be used to connect two judgments

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
Copy the code

MyPredicateOr:

package com.hcx.lambda;

import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/30. */
public class MyPredicateOr {
    public static boolean validateStr(String s, Predicate<String> pre1,Predicate<String> pre2){
// return pre1.test(s) || pre2.test(s);
        return pre1.or(pre2).test(s);
    }

    public static void main(String[] args) {
        String s = "acdef";
        boolean b = validateStr(s,str->str.length()>5,str->str.contains("a"));

        validateStr(s, new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return s.length()>5; }},new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return s.contains("a"); }}); System.out.println(b); }}Copy the code

Negate method: The Predicate interface has a negate method, which also means negate

    default Predicate<T> negate(a) {
        return(t) -> ! test(t); }Copy the code

MyPredicateNegate:

import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/30. */
public class MyPredicateNegate {

    public static boolean validateStr(String s, Predicate<String> pre){
// return ! pre.test(s);
        return pre.negate().test(s);
    }

    public static void main(String[] args) {
        String s = "acde";
        boolean b = validateStr(s,str->str.length()>5); System.out.println(b); }}Copy the code

MyTest:

package com.hcx.lambda;

import java.util.ArrayList;
import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/30. */
public class MyTest {

    public static ArrayList<String> filter(String[] arr, Predicate<String> pre1, Predicate<String> pre2) {
        ArrayList<String> list = new ArrayList<>();
        for (String s : arr) {
            boolean b = pre1.and(pre2).test(s);
            if(b) { list.add(s); }}return list;
    }

    public static void main(String[] args) {
        String[] array = {"Dilieba, female."."Gulinaza, female"."Tong Liya, female"."Zhao Liying, female"};
        ArrayList<String> list = filter(array,
                s -> s.split(",") [1].equals("Female"),
                s -> s.split(",") [0].length() == 4);
        for(String s : list){ System.out.println(s); }}}Copy the code
④ Conversion type: Function interface

Features: the input and output of Java. Util. The function. The function < T, R > interface is used to according to a type of data to get another type of data, the former is called precondition, the latter is called post-conditions.

The main abstract method in the Function interface is: R apply(T T), which obtains the result of type R based on the parameters of type T. The scenarios used are as follows: convert String to Integer.

package com.hcx.lambda;

import java.util.function.Function;

/** * Created by hongcaixia on 2019/10/30. */
public class MyFunction {

    public static void change(String str, Function<String,Integer> function){
// Integer i = function.apply(str);
        // Automatic unpacking Integer Automatically converts to int
        int i = function.apply(str);
        System.out.println(i);
    }

    public static void main(String[] args) {
        String s = "1234";
        change(s,str->Integer.parseInt(str));

        inti = Integer.parseInt(s); System.out.println(i); }}Copy the code

AndThen method:

package com.hcx.lambda;

import java.util.function.Function;

Function
      
        fun1 :Integer I = fun1. Apply ("123")+10; Function
       
         fun2 :String s = fun2.apply(I); * Created by hongcaixia on 2019/10/31. */
       ,string>
      ,integer>
public class MyFunctionTest {

    public static void change(String str, Function<String,Integer> fun1,Function<Integer,String> fun2){
        String string = fun1.andThen(fun2).apply(str);
        System.out.println(string);
    }

    public static void main(String[] args) {
        change("123",str->Integer.parseInt(str)+10,i->i+""); }}Copy the code

Custom function model stitching Demo:

package com.hcx.lambda;

import java.util.function.Function;

/** * String = "STR,20"; * 1. Truncate the numeric age part of the string to get the string; * 2. Convert the string from the previous step to an int; * 3. Add 100 to the int number in the previous step to get the int number. * Created by hongcaixia on 2019/10/31. */
public class MyFunctionTest2 {

    public static int change(String str, Function
       
         fun1,Function
        
          fun2, Function
         
           fun3)
         ,integer>
        ,integer>
       ,string>{
        return fun1.andThen(fun2).andThen(fun3).apply(str);
    }

    public static void main(String[] args) {
        int num = change("Zhao Liying,32",str->str.split(",") [1],
                str->Integer.parseInt(str),
                i->i+100); System.out.println(num); }}Copy the code

Note: Using an anonymous inner class generates an extra class after compilation, whereas using a lambda, with the invokedynamic instruction at the bottom, there are no extra classes

Method reference

A method reference can be used if a method already implements something in the lambda body. Method references are a simplification of lambda

MyPrintable:

public interface MyPrintable {
    void print(String str);
}
Copy the code

DemoPrint:

public class DemoPrint {

    private static void printString(MyPrintable data){
        data.print("Hello,World");
    }

    public static void main(String[] args) {
        printString(s->System.out.println(s));

        printString(new MyPrintable() {
            @Override
            public void print(String str) { System.out.println(str); }}); }}Copy the code

Improvement:

public class DemoPrint {

    private static void printString(MyPrintable data){
        data.print("Hello,World");
    }

    public static void main(String[] args) {
        //printString(s->System.out.println(s));printString(System.out::println); }}Copy the code

Method references

The double colon :: is the reference operator, and the expression it is in is called a method reference. If the function scheme Lambda is expressing already exists in an implementation of a method, then the method can be referred to by a double colon as an alternative to Lambda.

Three formats:

  • Object :: Instance method name
  • Class :: Static method name
  • Class :: Instance method name

Analysis of the

  • Lambda expression:s -> System.out.println(s);
  • Method Reference writing:System.out::println

The first way is to take the argument and pass it to the system.out.println method via Lambda. The second option: simply replace the Lambda with the println method in system.out.

Note: The argument list and return value types of the methods called in the lambda body are the same as those of the abstract methods of the functional interface. The arguments passed in a Lambda must be of the type that the method in the method reference can receive, or an exception will be thrown

3.1 Referring to member methods by object names

    @Test
    public void test(a){
        Person person = new Person();
        Supplier<String> supplier =() -> person.getName();
        System.out.println(supplier.get());

        Supplier<Integer> supplier1 = person::getAge;
        System.out.println(supplier1.get());
    }
Copy the code

MyPrintable:

public interface MyPrintable {
    void print(String str);
}
Copy the code

MethodReadObj:

public class MethodReadObj {
    public void printUpperCaseString(String str){ System.out.println(str.toUpperCase()); }}Copy the code

MethodReference1:

public class MethodReference1 {

    public static void printString(MyPrintable p){
        p.print("hello");
    }

    public static void main(String[] args) {
        printString(str-> {
            MethodReadObj methodReadObj = new MethodReadObj();
            methodReadObj.printUpperCaseString(str);
        });

        MethodReadObj object already exists * 2. The member method printUpperCaseString already exists * so you can reference the member method */ using the object name
        MethodReadObj methodReadObj = newMethodReadObj(); printString(methodReadObj::printUpperCaseString); }}Copy the code

3.2 Referring to static methods by class name

If the class already exists and the static method already exists, the static member method can be referenced directly by the class name

@Test
public void test1(a){
    Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
    Comparator<Integer> comparator1 = Integer::compare;
}
Copy the code

MyCalc:

public interface MyCalc {
    int calc(int num);
}
Copy the code

MethodRerference2:

public class MethodRerference2 {

    public static int method(int num,MyCalc c){
        return c.calc(num);
    }

    public static void main(String[] args) {
        int number = method(-10, num -> Math.abs(num));

        int number1 = method(-10, Math::abs); System.out.println(number); System.out.println(number1); }}Copy the code

Refer to instance methods by class name

@Test
public void test2(a){
    BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);
    BiPredicate<String,String> biPredicate1 = String::equals;
}
Copy the code

Note: In this case, certain conditions must be met: the first argument in the lambda expression is the caller in the lambda body, and the second argument is the argument in the lambda body

3.3 Referring to member methods through super

If inheritance exists, method references can also be used instead when a super call is needed in a Lambda. MyMeet :

@FunctionalInterface
public interface MyMeet {
    void meet(a);
}
Copy the code

Parent:

public class Parent {
    public void hello(a){
        System.out.println("Hello, I 'm the Parent"); }}Copy the code

Child:

public class Child extends Parent{
    @Override
    public void hello(a) {
        System.out.println("Hello, I 'm the Child");
    }

    public void method(MyMeet myMeet){
        myMeet.meet();
    }

    public void show(a){
        method(()->{
            Parent parent = new Parent();
            parent.hello();
        });

        // Call the parent class with the super keyword
        method(()->super.hello());

        /** * Use method references: use super to refer to a member method of the parent class: * 1. Super already exists * 2. The parent member method hello already exists * you can use super directly to refer to the parent member method */
        method(super::hello);
    }

    public static void main(String[] args) {
        newChild().show(); }}Copy the code

3.4 Referencing member methods through this

This represents the current object. If the method you want to refer to is a member method in the current class, you can use the format of this:: member method to refer to MyWallet:

@FunctionalInterface
public interface MyWallet {
    void buy(a);
}
Copy the code

BuyThing:

public class BuyThing {

    public void buyCar(a){
        System.out.println("Don't touch me when you get one.");
    }

    public void getSalary(MyWallet myWallet){
        myWallet.buy();
    }

    public void method(a){
        getSalary(()->this.buyCar());

        /** * 1. This already exists * 2. This member method buyCar already exists * so we can use this to refer to this member method buyCar */
        getSalary(this::buyCar);

    }

    public static void main(String[] args) {
        newBuyThing().method(); }}Copy the code

3.5 constructor references for class

Because the constructor name is exactly the same as the class name, it is not fixed. So the constructor reference is formatted with the class name ::new.

public void test3(a){
    Supplier<Person> personSupplier = ()->new Person();
    The constructor reference is a no-parameter constructor because the Supplier get method has no arguments
    Supplier<Person> personSupplier1 = Person::new;
}

public void test4(a){
    Function<Integer,Person> personFunction = (x)->new Person(x);
    // Constructor reference Is a constructor for an integer because the apply method in Function has only one argument
    Function<Integer,Person> personFunction1 = Person::new;
}
Copy the code

Note: The argument list of the constructor to be called is the same as the argument list of the abstract method in the functional interface

Person:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private String name;
}
Copy the code

PersonBuilder:

@FunctionalInterface
public interface PersonBuilder {
    // Create a Perosn object based on the passed name
    Person builderPerson(String name);
}
Copy the code

Demo:

public class Demo {

    // Pass the name and PersonBuilder interface to create a Person object from the name
    public static void printName(String name,PersonBuilder personBuilder){
        Person person = personBuilder.builderPerson(name);
        System.out.println(person.getName());
    }

    public static void main(String[] args) {
        printName("hongcaixia",str->new Person(str));

        1. Constructor new Person(String name) known * 2. You can use Person to reference new to create objects */
        printName("hongcaixia",Person::new); }}Copy the code

3.6 Array constructor references

Arrays are subclasses of Object, so they also have constructors with slightly different syntax. Format: Type [] : : new

public void test5(a) {
    Function<Integer, String[]> function = (x) -> new String[x];
    String[] strings = function.apply(10);

    Function<Integer,String[]> function1 = String[]::new;
    String[] strings1 = function1.apply(10);
}
Copy the code

ArrayBuilder:

@FunctionalInterface
public interface ArrayBuilder {
    // A method to create an array of type int, passing the length of the array, returns the created array of type int
    int[] builderArray(int length);
}
Copy the code

DemoArrayBuilder:

public class DemoArrayBuilder {

    public static int[] createArray(int length,ArrayBuilder arrayBuilder){
        return arrayBuilder.builderArray(length);
    }

    public static void main(String[] args) {
        int[] arr1 = createArray(5,length -> new int[length]);

        System.out.println(arr1.length);

        Int [] creates an array */ based on the length passed to it
        int[] arr2 = createArray(10.int[] : :new); System.out.println(Arrays.toString(arr2)); System.out.println(arr2.length); }}Copy the code

Four, StreamAPI

Streams are data channels that manipulate sequences of elements generated by data sources (collections, arrays, and so on).

package com.hcx.stream;

import java.util.ArrayList;
import java.util.List;

/** * Created by hongcaixia on 2019/10/31. */
public class MyStream1 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add(Eason Chan);
        list.add("Chen Xiaochun");
        list.add("Chung Hon-leung");
        list.add("Chen qi");
        list.add(William Chan);
        // Filter the beginning of the Chen, the name is three words
        List<String> chenList = new ArrayList<>();
        for(String item : list){
            if(item.startsWith("Chen")){
                chenList.add(item);
            }
        }

        List<String> threeList = new ArrayList<>();
        for(String item : chenList){
            if(item.length()==3){ threeList.add(item); }}// Traversal the output to meet the condition
        for(String item : threeList){
            System.out.println(item);
        }

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

        // Use Stream
        list.stream().filter(str->str.startsWith("Chen"))
                .filter(str->str.length()==3) .forEach(str-> System.out.println(str)); }}Copy the code

A Stream is a queue of elements from a data source

  • Elements are objects of a specific type that form a queue.
  • Data source Indicates the source of the flow. It could be collections, arrays, etc.
  • The Stream itself does not store elements, but evaluates them on demand.
  • Stream does not change the source object and can return a new Stream holding the result
  • Stream operations are deferred, meaning they wait until the result is needed

Unlike the previous Collection operation, the Stream operation has two basic characteristics:

  • Pipelining: All intermediate operations return the flow object itself. These operations can be cascaded into a pipe, as in fluent style. This allows you to optimize operations, such as delay and short-circuiting.
  • Internal iteration: Previously, collections were iterated explicitly outside the collection by Iterator or enhanced for. This is called external iteration. A Stream provides a way to iterate internally, and a Stream can call the traversal method directly.

The Stream operation has three steps:

  • Create a Stream: A data source (such as a collection, array) that retrieves a Stream
  • Intermediate operation: a chain of operations that processes data from a data source
  • Termination operation: A termination operation that performs an intermediate chain of operations and produces a result

Note that a “Stream” is a functional model of collection elements. It is not a collection, nor is it a data structure, and does not store any elements (or their address values) on its own.

import java.util.*;
import java.util.function.Predicate;

/** * Created by hongcaixia on 2019/10/31. */
public class MyPredicate {

    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(10.20.3, -5, -8);
        Collection<Integer> positiveNum = filter(nums,num->num>0);
        Collection<Integer> negativeNum = filter(nums,num->num<0);
        System.out.println(positiveNum);
        System.out.println(negativeNum);
        
    }

    private static <E> Collection<E> filter(Collection<E> source, Predicate<E> predicate){
        List<E> list = new ArrayList<>(source);
        Iterator<E> iterator = list.iterator();
        while (iterator.hasNext()){
            E element = iterator.next();
            if(!predicate.test(element)){
                iterator.remove();
            }
        }
        returnCollections.unmodifiableList(list); }}Copy the code

4.1 access to flow

Java.util.stream. stream

is a common stream interface added to Java8. The java.util.Collection interface uses the default stream method to fetch streams, so all of its implementation classes can fetch streams.

  • Stream () : Gets a serial stream
  • ParallelStream () : Gets parallel streams
package com.hcx.stream;

import java.util.*;
import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class GetStreamFromCollection {

    public static void main(String[] args) {
        
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Vector<String> vector = newVector<>(); Stream<String> stream3 = vector.stream(); }}Copy the code

The java.util.Map interface is not a subinterface of Collection, and its K-V data structure does not match the single feature of stream elements. Therefore, the corresponding stream needs to be divided into key, value or entry.

package com.hcx.stream;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class GetStreamFromMap {

    public static void main(String[] args) {
        Map<String,String> map = newHashMap<>(); Stream<Map.Entry<String, String>> stream1 = map.entrySet().stream(); Stream<String> stream2 = map.keySet().stream(); Stream<String> stream3 = map.values().stream(); }}Copy the code

If you use an array instead of a collection or a map, you cannot add a default method to an array object, so the Stream interface provides a static method of:

package com.hcx.stream;

import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class GetStreamFromArray {

    public static void main(String[] args) {
        String[] array = {Eason Chan."Chung Hon-leung".Miriam Yeung}; Stream<String> stream = Stream.of(array); }}Copy the code

④ Get infinite flow

public void test6(a) {
    Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
}

public void test7(a) {
    Stream<Double> stream = Stream.generate(() -> Math.random());
}
Copy the code

Note: The argument to the of method is a mutable argument, so arrays are supported.

Conclusion:

  • All of theCollectionAll sets can pass throughstreamThe default method gets the stream;
  • StreamStatic methods of the interfaceofYou can get the corresponding stream of the array

4.2 Common Methods of flow

Methods can be divided into two types:

  • Delay methodThe return value type is stillStreamA method of the interface’s own type, and therefore supports chained calls. (All methods except the finalizer are delay methods.)
  • Put an end to the methodThe return value type is no longerStreamMethods of the interface’s own type, so chain calls like StringBuilder are no longer supported. Finalizing methods include the count and forEach methods.

① One by one: forEach

void forEach(Consumer<? super T> action);
Copy the code

This method receives a Consumer interface function that hands each stream element to the function for processing. Consumer is a consumptive functional interface that passes lambda expressions and consumes data

package com.hcx.stream;

import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class StreamForEach {

    public static void main(String[] args) {
        Stream<String> stream = Stream.of("Zhang"."Bill"."Fifty"); stream.forEach(str-> System.out.println(str)); }}Copy the code

② Filter: Filter can be used to change a stream into another subset stream. Method signature:

Stream<T> filter(Predicate<? super T> predicate);
Copy the code

The interface accepts a Predicate function interface argument (which can be a Lambda or method reference) as a filter condition.

Java. Util. Stream. The Predicate functional interface only abstract method is: the Boolean test (T, T); This method yields a Boolean value indicating whether the specified condition is met: if the result is true, the filter method of the Stream Stream will retain the element; If the result is false, the filter method will discard the element.

public class StreamFilter {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of(Eason Chan.William Chan."Chen qi"."Chung Hon-leung");
        Stream<String> stream1 = stream.filter(str -> str.startsWith("Chen")); stream1.forEach(str-> System.out.println(str)); }}Copy the code

Note: a Stream is a piped Stream and can only be consumed once. When the Stream calls the method, the data flows back to the next Steam, and the first Stream is already used and closed, so the first Stream can no longer call the method.

③ Mapping: A map receives a lambda, converts the element to another form or extracts information, and takes as an argument a function that is applied to each element and maps it to a new element. Map elements in a stream to another stream. Method signature:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Copy the code

This interface requires a Function Function interface argument that converts type T data from the current stream to another type R stream. Java. Util. Stream. The Function, functional interface, the only method is: abstract R apply (T, T); This can convert a T type to an R type, and the action of conversion is called mapping.

public class StreamMap {

    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1"."2"."3"); Stream<Integer> integerStream = stream.map(str -> Integer.parseInt(str)); integerStream.forEach(i-> System.out.println(i)); }}Copy the code
    public void test8(a) {
        Person person = new Person("hcx".24);
        Person person1 = new Person("hcx2".24);
        List<Person> list = new ArrayList<>();
        list.add(person);
        list.add(person1);
        list.stream().map(Person::getName).forEach(System.out::println);
    }
Copy the code

④flatMap takes a function as a parameter, replaces each value in the stream with another stream, and then joins all streams into a single stream. The simple explanation is to convert several small lists into one large list. For example: [[‘ a ‘, ‘b’], [‘ c ‘, ‘d’]] – > [‘ a ‘, ‘b’, ‘c’, ‘d’] if we use the commonly used the map () method to obtain lowercaseWords data structure as follows: [[‘ a ‘, ‘b’, ‘c’], [‘ m ‘, ‘d’, ‘w’], [‘ k ‘, ‘e’, ‘t’]]. If we want to get, such as: ‘a’, ‘b’, ‘c’, ‘m’, ‘d’, ‘w’, ‘k’, ‘e’, ‘t’] such data structure of data, you need to use flatMap () method.

public void test9(a) {
    List<String> list = Arrays.asList("a"."b"."c");
    Stream<Stream<Character>> streamStream = list.stream().map(MethodReference::filterCharacter);
    streamStream.forEach((stream)->stream.forEach(System.out::println));

    / / use flatMap
    Stream<Character> characterStream = list.stream().flatMap(MethodReference::filterCharacter);
    characterStream.forEach(System.out::println);
}

public static Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();
    for(Character c : str.toCharArray()){
        list.add(c);
    }
    return list.stream();
}
Copy the code

The reduce protocol combines elements in the stream repeatedly to get a value

import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class StreamReduce {

    public static void main(String[] args) {
        sum(1.2.3.4.5);
    }

    private static void sum(Integer... nums){ Stream.of(nums).reduce(Integer::sum).ifPresent(System.out::println); }}Copy the code
@Test
public void test10(a) {
    List<Integer> list = Arrays.asList(1.2.3.4.5);
    Integer sum = list.stream().reduce(0,(x,y)->x+y);
    System.out.println(sum);
}
Copy the code

The final method stream provides a count method to count the number of elements. This method returns a long value representing the number of elements:

package com.hcx.stream;

import java.util.ArrayList;
import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class StreamCount {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        Stream<Integer> stream = list.stream();
        longcount = stream.count(); System.out.println(count); }}Copy the code

Limit method can be used to intercept convection, only the first N. Method signature:

Stream<T> limit(long maxSize);
Copy the code

The parameter is a long. If the current length of the collection is greater than the parameter, it is truncated. Otherwise, no operation is performed to delay the method, just intercepting the elements in the Stream, returning a new Stream, and continuing to call other methods in the Stream

public class StreamLimit {
    public static void main(String[] args) {
        String[] str = {"1"."2"."3"."4"."5"};
        Stream<String> stream = Stream.of(str);
        Stream<String> limitStream = stream.limit(3); limitStream.forEach(string-> System.out.println(string)); }}Copy the code

If you want to skip the first few elements, you can use the skip method to get a new truncated stream:

Stream<T> skip(long n);
Copy the code

If the current length of the stream is greater than n, the first n are skipped; Otherwise you get an empty stream of length 0

public class StreamSkip {
    public static void main(String[] args) {
        String[] str = {"1"."2"."3"."4"."5"};
        Stream<String> stream = Stream.of(str);
        Stream<String> skipStream = stream.skip(3); skipStream.forEach(string-> System.out.println(string)); }}Copy the code

If you have two streams that you want to merge into one Stream, you can use concat, the static method of the Stream interface

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
Copy the code

Note: This is a static method, unlike the concat method in java.lang.String

public class StreamConcat {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of(Eason Chan.William Chan."Chen qi"."Chung Hon-leung");
        String[] arr = {"1"."2"."3"}; Stream<String> stream2 = Stream.of(arr); Stream<String> concatStream = Stream.concat(stream1, stream2); concatStream.forEach(str-> System.out.println(str)); }}Copy the code

⑩ Sorted: sorted

  • Sorted () is sorted naturally
  • Sorted (Comparator com) custom sort
  • AllMatch checks whether all elements match
  • AnyMatch checks whether at least one element matches
  • NoneMatch checks to see if all elements are not matched
  • 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
public class StreamSort {
    public static void main(String[] args) {
        Integer[] nums = {2.9.0.5, -10.90}; Stream<Integer> numsStream = Stream.of(nums); Stream<Integer> sortedStram = numsStream.sorted(); sortedStram.forEach(num -> System.out.println(num)); }}Copy the code

⑪Collect transforms the stream into something else. Receives an implementation of a Collector that summarizes the elements in the stream

import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** * Created by hongcaixia on 2019/10/31. */
public class StreamCollect {

    public static void main(String[] args) {
        List<Integer> list = Stream.of(1.2.3.4.5).collect(Collectors.toList());
        List<Integer> list1 = Stream.of(1.2.3.4.5).collect(LinkedList::new,List::add,List::addAll);
        System.out.println(list.getClass());//class java.util.ArrayList
        System.out.println(list1.getClass());//class java.util.LinkedList}}Copy the code
@Test
public void test11(a) {
    Person person = new Person("hcx".24);
    Person person1 = new Person("hcx2".24);
    List<Person> list = new ArrayList<>();
    list.add(person);
    list.add(person1);
    List<String> collect = list.stream().map(Person::getName).collect(Collectors.toList());
}
Copy the code

4.3 Parallel flow and Sequential flow

A parallel stream is a stream that splits a piece of content into chunks of data and processes each chunk separately with a different thread. Parallel () and sequential() allow switching between parallel and sequential streams.

@Test
public void test12(a) {
    / / order flow
    LongStream.rangeClosed(0.100).reduce(0,Long::sum);
    / / parallel flows
    long reduce = LongStream.rangeClosed(0.100).parallel().reduce(0, Long::sum);
    / / 5050
    System.out.println(reduce);
}
Copy the code