Responsive programming

1, Java8 functional programming / Lambda

1. What is a lambda expression

lambda expressions are essentially anonymous methods. Let's look at the following example:

public int add(int x, int y) {
    return x + y;
}

After being converted to lambda expression, it looks like this:

(int x, int y) -> {
    return x + y;
}

The parameter type can also be omitted, and the Java compiler will infer according to the context:

(x, y) -> { return x + y; } //Explicitly indicate the return value
 perhaps
(x, y) -> x + y; // If the statement block has only one statement, {} and return can be omitted

It can be seen that a lambda expression consists of three parts: a parameter list, an arrow (- >), and an expression or statement block.

In the following example λ The expression has no parameters and no return value (equivalent to a method accepting 0 parameters and returning void, which is actually an implementation of the run method in Runnable):

() -> { System.out.println("Hello Lambda!"); }

If there is only one parameter and the type can be inferred by Java, the parentheses in the parameter list can also be omitted:

list -> { return list.size(); }

2. Type of lambda expression

The target type of lambda expression is "functional interface", which is a new concept introduced in Java 8. It is defined as an interface. If there is only one explicitly declared abstract method, it is a functional interface. Generally, it is marked with @ functional interface (or not). Examples are as follows:

@FunctionalInterface
public interface Runnable { 
   void run(); 
}

The lambda expression returns the object instance that implements the function interface. You can assign a value to a functional interface with a lambda expression:

Runnable r1 = () -> {System.out.println("Hello Lambda!");};

Then assign a value to an Object:

Object obj = r1;

But you can't do this:

Object obj = () -> {System.out.println("Hello Lambda!");}; // ERROR! Object is not a functional interface!

It must be explicitly transformed into a functional interface to:

Object o = (Runnable) () -> { System.out.println("hi"); }; // correct

A lambda expression can only be used as an Object after it is transformed into a functional interface. Therefore, the following sentence cannot be compiled:

System.out.println( () -> {} ); //Wrong! Unknown target type

Must first transform:

System.out.println( (Runnable)() -> {} ); // correct

As like as two peas, you write a functional interface, which is exactly the same as Runnable.

@FunctionalInterface
public interface MyRunnable {
    public void run();
}

that

Runnable r1 =    () -> {System.out.println("Hello Lambda!");};
MyRunnable2 r2 = () -> {System.out.println("Hello Lambda!");};

They are all written correctly. This shows that a lambda expression can have multiple target types (functional interfaces), as long as the function matches successfully.
Note, however, that a lambda expression must have at least one target type.

JDK predefines many functional interfaces to avoid repeated user definitions. The most typical is Function:

@FunctionalInterface
public interface Function<T, R> { 
    R apply(T t);
}

This interface represents a function, accepts a parameter of type T and returns a return value of type R.

Another predefined functional interface is called Consumer. The only difference from Function is that it has no return value.

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

There is also a Predicate to judge whether a condition is met. Often used for screening operations:

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

Example code:

package test;

import java.text.DecimalFormat;
import java.util.function.*;

@FunctionalInterface
interface IMoneyFormat {
	String format(int money);
}

class MyMoney {
	private int money;
	
	public MyMoney(int money) {
		this.money = money;
	}
	
	public void printMoney(IMoneyFormat moneyFormat) {
		System.out.println("My deposit:" + moneyFormat.format(this.money));
	}
	
//	public void printMoney(Function<Integer, String> moneyFormat) {
//		System.out.println("my deposit:" + moneyFormat.apply(this.money));
//	}
}

public class JDKFunctionDemo {

	public static void main(String[] args) {
		
		// 1. Use custom function interface
		MyMoney me = new MyMoney(1000000);
		me.printMoney((i)-> {
			return new DecimalFormat("#,###").format(i);
		});
		
//		Function<Integer, String> moneyFormat = i-> new DecimalFormat("#,###").format(i);
//		//Function interface chained operation
//		me. Printmoney (moneyformat. Andthen (s - > "RMB" + s));
		
		// 2. Common function interfaces of jdk8
		// Function < T, R >, enter t to return the function of R
        Function<Integer, Integer> function = i -> i * 2;
        System.out.println(function.apply(5));
        
    	// Unaryoperator < T >, functions with the same input and output types (unary functions)
		UnaryOperator<Integer> unaryOperator = i -> i * 2;
        System.out.println(unaryOperator.apply(5));
        
        // Both input and output types are functions of Integer
        IntUnaryOperator intUnaryOperator = i -> i * 2;
        System.out.println(intUnaryOperator.applyAsInt(5));
        
		// Consumer < T >, no function returned by input t (consumer)
		Consumer<String> consumer = i -> System.out.println(i);
		consumer.accept("hello word");
		
		// Supplier < T >, function (provider) that returns t without input
		Supplier<String> supplier = () -> new String("hello word");
		System.out.println(supplier.get());
		
		// Predicate < T >, return Boolean function (assertion)
		Predicate<Integer> predicate = i -> i > 10;
		System.out.println(predicate.test(5));
		
		// Bifunction < T, u, R >, input (T, U) function that returns R (2 input functions)
		BiFunction<Integer, Integer, Integer> biFunction = (i, j) -> i * j;
		System.out.println(biFunction.apply(5, 5));
		
		// Binaryoperator < T >, functions with the same input and output types (binary functions)
		BinaryOperator<Integer> binaryOperator = (i, j) -> i * j;
		System.out.println(binaryOperator.apply(5, 5));
	}
}

3. Method reference

Any lambda expression can represent the anonymous descriptor of the unique method of a functional interface. We can also use a specific method of a class to represent this descriptor, which is called method reference. For example:

Integer::parseInt //Static method reference
System.out::print //Instance method reference
Person::new       //Constructor reference

Example code:

package test;

import java.util.function.Consumer;
import java.util.function.*;

class Dog {
	private String name = "Howling dog";
	private int food = 10; // Default 10 kg dog food
	
	public Dog() {
		
	}
	
	public Dog(String name) {
		this.name = name;
	}
	
	/**
	 * Static method
	 * dog's bark
	 * @param dog
	 */
	public static void bark(Dog dog) {
		System.out.println(dog + "Yes");
	}
	
	/**
	 * For non static methods, the compiler will pass the current object instance as the first parameter into each non static method by default, and the parameter name is this
	 * Eat a bitch
	 * @param num A few Jin
	 * @return How many kilograms are left
	 */
	public int eat(int num) {
		System.out.println("Yes"+ num + "Kg dog food");
		this.food -= num;
		return this.food;
	}
	
	/**
	 * Eat a bitch
	 * @param num A few Jin
	 * @return How many kilograms are left
	 */
	public int eat2(Dog this, int num) {
		System.out.println("Yes"+ num + "Kg dog food");
		this.food -= num;
		return this.food;
	}
	
	@Override
	public String toString() {
		return this.name;
	}
}

public class MethodReferenceDemo {

	public static void main(String[] args) {
		
		Dog dog = new Dog();

		// Method reference
		Consumer<String> consumer = System.out::println; 
		consumer.accept("hello word");
		
		// Static method, using class name to reference method
		Consumer<Dog> consumerDog = Dog::bark;
		consumerDog.accept(dog);
		
		// Non static method, using the method reference of the object instance
		Function<Integer, Integer> function = dog::eat;
		System.out.println("Remaining" + function.apply(3) + "Jin");
		
		// Non static method, using class name to reference method
		BiFunction<Dog, Integer, Integer> biFunction = Dog::eat2;
		System.out.println("Remaining" + biFunction.apply(dog, 3) + "Jin");
		
		// Method reference of parameterless constructor
		Supplier<Dog> supplier = Dog::new;
		System.out.println("A new object was created:" + supplier.get());
		
		// Method reference of constructor with parameters
		Function<String, Dog> functionDog = Dog::new;
		System.out.println("New object created:" + functionDog.apply("Wangcai"));
	}
}

4. Cascading expressions and corrilization

Coriolism: convert a function with multiple parameters into a function with only one parameter

Purpose of coriolism: function Standardization (after coriolism, all functions have only one parameter)

Example code:

package test;

import java.util.function.*;

/**
 * Cascading expressions and corrilization
 * Coriolism: convert a function with multiple parameters into a function with only one parameter
 * Purpose of coriolism: function Standardization (after coriolism, all functions have only one parameter)
 * @author think
 *
 */
public class CurryDemo {

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void main(String[] args) {
	    
	   // Enter two parameters and return one parameter
	   BiFunction<Integer, Integer, Integer> biFunction = (x, y) -> x + y;
	   System.out.println(biFunction.apply(2, 3));
	   
		// The cascade expression of x + y is realized
       Function<Integer, Function<Integer, Integer>> function = x -> y -> x + y;
       System.out.println(function.apply(2).apply(3));
       
       // The cascade expression of x + y + z is realized
       Function<Integer, Function<Integer, Function<Integer, Integer>>> function2 = x -> y -> z -> x + y + z;
       System.out.println(function2.apply(2).apply(3).apply(4));
       
       int[] nums = {2, 3, 4};
       Function f = function2;
       for (int i = 0; i < nums.length; i++) {
    	   if (f instanceof Function) {
    		   Object obj = f.apply(nums[i]);
    		   if (obj instanceof Function) {
    			   f = (Function) obj;
    		   } else {
    			   System.out.println("End of call:The result is" + obj);
    		   }
    	   } 
       }
	}
}

To sum up, a lambda expression actually defines an anonymous method, but the method must conform to at least one functional interface.

2, Java8 Stream programming / Stream

1. What is a Stream

Stream is not a collection element. It is not a data structure and does not save data. It is about algorithms and calculations. It is more like an advanced version of Iterator. In the original version of Iterator, users can only explicitly traverse elements one by one and perform some operations on them; In the advanced version of stream, users only need to give what operations they need to perform on the elements contained, such as "filter out strings with a length greater than 10" and "get the first letter of each string". The stream will implicitly traverse internally and make corresponding data conversion.

2. Three steps of Stream operation

Create Stream
Get a Stream from a data source. For example, the stream() method in the List can directly return a Stream object.

Intermediate operation
We need operations on the data in the stream, such as loop processing (map), filtering (filter), etc

Terminate operation
Streams are evaluated lazily. As we will talk about later, a termination operation is required to return the data after the intermediate operation.

As shown below:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-aqcsiybp-1626679560257) (C: \ users \ think \ appdata \ roaming \ typora \ user images \ image-20210609145725205. PNG)]

be careful:

Stream is just a calculation channel and does not store elements by itself;

Streams do not change the source object; instead, they return a new Stream object.

The Stream operation is delayed and will only be executed when the termination operation is executed.

3. How to create Steam

3.1. Create a stream by the Collection subclass

java8 extends the Collection interface and provides two methods: stream (return sequential stream) and parallel stream (return parallel stream).

Example code:

@Test
public void test(){
    List<String> list = Arrays.asList("a","b","c");
    Stream stram = list.stream();
    Stream parallelSteam = list.parallelStream();
}

3.2. Create flow from data

Array can obtain a Steam object through the stream method of the Arrays tool class

Example code:

@Test
public void test() {
    String[] strArr = new String[]{"a","b","c"};
    Stream stram = Arrays.stream(strArr);
    // There are also many overloaded methods that can return streams with types, such as:
    IntStream stram2 = Arrays.stream(new int []{1,2,3,4,5});
}

3.3. Create flow by specific value

Through the static method of Stream Of (t... values) can create a Stream that can accept any value

Code example:

@Test
public void test() {
    String[] strArr = new String[]{"a","b","c"};
    Stream stram = Stream.of(strArr);
}

3.4. Create flow through function (infinite flow)

Through stream Iterate() and stream The generate () method creates an infinite stream

Code example:

@Test
public void test4() {
    // 1. Stream.iterate method the first method represents the starting value, and the second parameter needs to provide a unary operation function, which we pass to it with a lambda expression
    Stream stream1 = Stream.iterate(0, (x) -> x + 2);
    stream1.forEach(System.out::println); //The output is 0,2,4,6,8,10 Will continue to cycle, never stop
   
    // 2. Stream.generate requires a supply function interface
    Stream stream2 = Stream.generate(() -> 1);
    stream2.forEach(System.out::println); //Output countless 1, will continue to cycle, never stop
}

Note: in practice, we certainly will not generate an infinite Stream. Unless you want an endless loop, we will combine the termination operation of Stream, such as limit, to obtain a Stream with a specified number of elements:

@Test
public void test5(){
    // We get the first 50 even numbers from 0
    Stream stream1 = Stream.iterate(0, (x) -> x + 2).limit(50);
    stream1.forEach(System.out::println);  //Output 0, 2, 4, 6, 8 ninety-eight
}

4. Intermediate operation of Stream

Stream can perform a series of pipelined intermediate operations. Unless the termination operation is triggered on the pipeline, these intermediate operations will not be processed, but will be processed at one time when the operation is terminated. This is called the inert evaluation of stream.

Remember, no matter how many times the intermediate operation is done, it will not change the original flow, but only return a new flow;

The intermediate operations of Stream can be divided into the following categories:

4.1 intermediate operation: screening and slicing

Method description filter (predict d) accepts an assertion function to process the elements in the Stream stream, filter out the elements that do not meet the conditions, filter the elements differently, remove the duplicate elements through the hasCode and equals methods in the Stream element, limit(long maxSize) cut off the Stream, so that the elements do not exceed the number specified in manSize, skip(Long n) skip the elements, Returns a Stream that discards the first n elements. If there are less than n elements in the Stream, an empty Stream will be returned

Code example:

package test;

import java.util.Arrays;
import java.util.List;

/**
 * Employee information
 */
class Employee {
	
	private String name;   // full name
	private int age;       // Age
	private double salary; // wages
	
	public Employee(String name, int age, double salary) {
		this.name = name;
		this.age = age;
		this.salary = salary;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + "]";
	}
}

/**
 * Intermediate operation exercise of stream programming (filtering and slicing)
 * There are three steps in stream operation: 1 Create 2 Intermediate operation 3 Terminate operation
 * There must be one termination operation in flow programming, and there can only be one termination operation, and there can be (0-n) intermediate operations
 * Creation of flow
 *    1.Create through Collection
 *    2.Create with Arrays
 *    3.Through stream Of() static method to get stream
 *    4.Create infinite flow (iteration, build)
 * Intermediate operation of stream 
 *    Stateless filter(), map(), flatMap() depends on the element
 *    Stateful distinct() sorted(), limit(), 
 * Termination of flow (end of flow)
 *    Terminate operation: find and match allmatch, anymatch, nonematch, findfirst, findany, count, Max, min, foreach
 *    Terminate operation: rule reduce
 *    Terminating operations: collecting  
 */
public class StreamMiddleOperateDemo {

	public static void main(String[] args) {
		
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 18, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );
		
		// 1. Filter out employees younger than 25
		System.out.println("Filter out employees younger than 25");
        emps.stream().filter((e) -> e.getAge() > 25).forEach(System.out::println);
        
        // 2. First obtain the top 3 employees, and then obtain the employees older than 25. (intermediate operations can be performed any time)
    	System.out.println("\n First obtain the top 3 employees, and then obtain the employees older than 25");
        emps.stream().filter(e -> e.getAge() > 25).limit(3).forEach(System.out::println);
        
        // 3. Filter out employees with duplicate names
		System.out.println("\n Filter out employees with duplicate names");
        emps.stream().distinct().forEach(System.out::println);
        
        // 4. Employees after getting the third place
		System.out.println("\n Employees after getting the third place");
        emps.stream().skip(3).forEach(System.out::println);
	}
}

4.2 intermediate operation: mapping

Method description map(Function f) accepts a function interface as a parameter. The function will process each element in the stream and return the processed stream mapToDouble(ToDoubleFunction f) interface. A function interface is used as a parameter. The function will process each element in the stream and return a Double value, Finally, a Stream mapToInt(ToIntFunction f) interface and a function interface are obtained as parameters. The function will process each element in the stream and return an Int value. Finally, a Stream mapToLong(ToLongFunction f) interface and a function interface are obtained as parameters. The function will process each element in the stream and return a Long value, Finally, a Stream flatMap(Function f) is obtained, which accepts a function as a parameter, converts each value in the stream into a new stream, and finally connects these streams together

Code example:

package test;

import java.util.Arrays;
import java.util.List;

/**
 * Intermediate operation practice of stream programming (mapping)
 */
public class StreamMiddleOperateDemo {

	public static void main(String[] args) {
		
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 18, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );
		
        // 1. Obtain the names of all employees
        System.out.println("\n Get the names of all employees");
        emps.stream().map(e -> e.getName()).forEach(System.out::println);
        
        // 2. Get the salary of all employees. Here, the salary is Double. We can use mapToDouble method
        System.out.println("\n Get the salary of all employees");
        emps.stream().mapToDouble(e -> e.getSalary()).forEach(System.out::println);
        
        // 3. Get the age of all employees and use mapToInt method
        System.out.println("\n Get the age of all employees");
        emps.stream().mapToInt(e -> e.getAge()).forEach(System.out::println);
	}
}

4.3 intermediate operation: sorting

Method description sorted returns a new stream, and the elements in the stream are sorted according to natural sorting. sorted(Comparator comp) returns a new stream, and the sorting method specified by the Comparator is sorted

Code example:

package test;

import java.util.Arrays;
import java.util.List;

/**
 * Intermediate operation practice of flow programming (sorting)
 */
public class StreamMiddleOperateDemo {

	public static void main(String[] args) {
		
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 18, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );
        
        // 1. Sort by salary
        System.out.println("\n Sort by salary");
        emps.stream().sorted((x, y) -> Double.compare(x.getSalary(), y.getSalary()))
            .forEach(System.out::println);
	}
}    

5. Termination of Stream

The termination operation of Stream is used to obtain the final result of a series of pipeline operations. The result can be any value, such as boolean, List, Integer or even void. The termination operations are also divided into the following categories:

5.1 terminate: find and match

Method description allmatch (predict P) passes in an assertion function to judge all elements in the stream. If they are satisfied, it returns true, otherwise it returns false. Anymatch (predict P) passes in an assertion function to judge all elements in the stream. As long as one of the conditions is met, it will return true. If none of the conditions are met, it will return false. Nonematch (predict P) returns true if all conditions are not met, otherwise false. findFirst() returns the first element in the stream. findAny() returns any element in the stream. count() returns the number of elements in the stream. After max(Comparator c) is sorted according to the given sorting rule, min(Comparator c) returns the element with the maximum value in the flow. After min(Comparator c) is sorted according to the given sorting rule, it returns the internal iteration of the element with the minimum value in the flow forEach(Consumer c).

Code example:

package test;

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

/**
 * Stream programming termination operation exercise (find and match)
 * There are three steps in stream operation: 1 Create 2 Intermediate operation 3 Terminate operation
 * There must be one termination operation in flow programming, and there can only be one termination operation, and there can be (0-n) intermediate operations
 * Creation of flow
 *    1.Create through Collection
 *    2.Create with Arrays
 *    3.Through stream Of() static method to get stream
 *    4.Create infinite flow (iteration, build)
 * Intermediate operation of stream 
 *    Stateless filter(), map(), flatMap() depends on the element
 *    Stateful distinct() sorted(), limit(), 
 * Termination of flow (end of flow)
 *    Terminate operation: find and match allmatch, anymatch, nonematch, findfirst, findany, count, Max, min, foreach
 *    Terminate operation: rule reduce
 *    Terminating operations: collecting  
 */
public class StreamEndOperateDemo {

	public static void main(String[] args) {
         
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 17, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );

		 // 1. Check whether any employees are older than 18
        boolean flag1 = emps.stream().allMatch(e -> e.getAge() > 18);
        System.out.println("\n Check whether any employees are older than 18: " + flag1);   // false
        
        // 2. Are there any employees older than 50 (except Zhang San)
        boolean flag3 = emps.stream().filter(e -> !"Zhang San".equals(e.getName())).anyMatch(e -> e.getAge() > 50);
        System.out.println("\n Are there any employees older than 50(Except Zhang San): " + flag3);  //true
        
        // 3. No employee is older than 50?
        boolean flag4 = emps.stream().noneMatch(e -> e.getAge() > 50);
        System.out.println("\n No employee is older than 50: " + flag4);  //false
        
        // 4. Sort by age first, and then return to the first employee. optional is an object that Java 8 uses to wrap objects that may have null pointers
        Optional<Employee> op1 = emps.stream().sorted((x, y) -> Integer.compare(x.getAge(), y.getAge())).findFirst();
        System.out.println("\n Sort by age first, and then return to the first employee: " + op1.get()); 
        
        // 5. Find the name of any employee. When using sequential flow, the first object will be returned. When using parallel flow, the name of an employee will be returned randomly
        Optional<String> op2 = emps.parallelStream().map(e -> e.getName()).findAny();
        System.out.println("\n Find the name of any employee: " + op2.get()); //An employee will be randomly selected
        
        // 6. Query the number of employees
        Long count = emps.stream().count();
        System.out.println("\n Query the number of employees: " + count);
        
        // 7. Query the information of the employee with the highest salary. PS: this can also be achieved by sorting by salary first, and then taking the first element
        Optional<Employee> maxSalary = emps.stream().max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
        System.out.println("\n Query employee information with the highest salary: " + maxSalary.get());
        
        // 8. Query the minimum age of employees
        Optional<Employee> minAge = emps.stream().max((x, y) -> -Integer.compare(x.getAge(), y.getAge()));
        System.out.println("\n Query employee minimum age: " + minAge.get());
        
        // 9. Circularly output the information of all employees
        System.out.println("\n Loop out the information of all employees");
        emps.stream().forEach(System.out::println);
	}
}

5.2 termination: Protocol

Method description reduce(T iden, BinaryOperator bo) can combine the elements in the stream repeatedly to get a value, and return T reduce(BinaryOperator bo). It can combine the elements in the stream repeatedly to get a value and return Optional. (Optional, we'll talk later)

Code example:

package test;

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

/**
 * Stream programming termination operation exercise (Protocol)
 */
public class StreamEndOperateDemo {

	public static void main(String[] args) {
         
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 17, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );
        
        // 1. Add the name of all employees to the name of the next employee, such as Zhang San and Li Si
        Optional<Employee> op3 = emps.stream().reduce((x,y) -> {
            x.setName(x.getName() + "|" + y.getName()); 
        	return x;
        });
        System.out.println(op3.get().getName());  //Zhang San, Li Si, Wang Wu, Tian Qi, Zhao Liu, Zhao Liu
        
        // 2. Add the name of all employees to the name of the next employee, and start with Wang Ba;
        Employee emp = emps.stream()
                .reduce(new Employee("bastard", 65, 8888.88)
                        , (x,y) -> {
                            x.setName(x.getName() + "|" + y.getName());
                            return x;
                        });
        System.out.println(emp.getName());  //Wang Ba, Zhang San, Li Si, Wang Wu, Tian Qi, Zhao Liu, Zhao Liu
	}
}

5.3 termination: Collection

Method description collect(Collector c) converts the elements in the Stream into other forms, accepts the implementation of a collector interface, and is used to process the elements in the Stream stream and convert the Stream into objects in other forms.

The implementation of the method in the collector interface determines how to perform collection operations (such as collecting List, Set, Map) on the stream. The Collectors class in java8 provides many static methods to easily create common collector instances. The specific methods and instances are as follows:

Code example:

package test;

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Stream programming termination operation exercise (Collection)
 */
public class StreamEndOperateDemo {

	public static void main(String[] args) {
         
		List<Employee> emps = Arrays.asList(
	            new Employee("Zhang San", 17, 6666.66),
	            new Employee("Li Si", 20, 7777.77),
	            new Employee("Wang Wu", 36, 8888.88),
	            new Employee("pseudo-ginseng", 55, 11111.11),
	            new Employee("Zhao Liu", 55, 9999.99),
	            new Employee("Zhao Liu", 45, 12222.22)
	    );
        
		// 1. Sort by age, collect it into a list and return it
		System.out.println("\n Sort by age and collect into one list And return");
        List<Employee> list = emps.stream().sorted((x, y) -> Integer.compare(x.getAge(), y.getAge()))
                  .collect(Collectors.toList());
        list.forEach(System.out::println);
        
        // 2. Calculate the number of elements in the flow:
        long count = emps.stream().collect(Collectors.counting());
        System.out.println("\n Count the number of elements in the flow: " + count);
        
        // 3. Sum the ages of all employees:
        int inttotal = emps.stream().collect(Collectors.summingInt(Employee::getAge));
        System.out.println("\n Sum the ages of all employees: " + inttotal);
        
        // 4. Calculate the average salary of all employees:
        Double doubleavg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println("\n Calculate the average salary of all employees: " + doubleavg);
        
        // 5. Return an IntSummaryStatistics. You can obtain statistical values through this object, such as average value:
        IntSummaryStatistics iss = emps.stream().collect(Collectors.summarizingInt(Employee::getAge));
        System.out.println("\n average value: " + iss.getAverage());
        System.out.println("Maximum: " + iss.getMax());
        System.out.println("minimum value: " + iss.getMin());
        
        // 6. Connect the names of all employees:
        String str= emps.stream().map(Employee::getName).collect(Collectors.joining());
        System.out.println("\n Connect the names of all employees: " + str);
        
        // 7. It is equivalent to sorting by salary first, and then taking out the first employee
        Optional<Employee> min = emps.stream().collect(
            Collectors.minBy((x ,y) -> Double.compare(x.getSalary(), y.getSalary()))
        );
        System.out.println("\n The lowest paid employee: " + min);
        
        // 8. Group the results according to an attribute. The attribute is K and the result is V:
        Map<String, List<Employee>> kv = emps.stream().collect(
            Collectors.groupingBy(Employee::getName)
        );
        System.out.println("\n The results are grouped according to an attribute. The attribute is K,The result is V: \n" + kv);
        
        // 9. Partition according to true or false. Points older than 30 are in the true area and points younger than 30 are in the false area
        Map<Boolean,List<Employee>> vd = emps.stream().collect(
            Collectors.partitioningBy(e -> e.getAge() > 30)
        );
        System.out.println("\n according to true or false The score of those older than 30 is true Zone, less than 30 points in false area: \n" + vd);
	}
}

The summary of Stream is actually a sentence. Remember the three steps of Stream operation, create Stream - > a series of intermediate operations - > terminate operations to get the returned results.

3, Java9 responsive stream programming / Reactive Stream

Java 9's Reactive Streams is an implementation of asynchronous streaming programming. It is based on asynchronous publish and subscribe model and has the characteristics of non blocking "back pressure" data processing.

Non blocking back pressure: it is a mechanism that allows subscribers in the publish subscribe model to avoid receiving large amounts of data (exceeding their processing capacity). Subscribers can asynchronously notify publishers to reduce or increase the rate of data production and publication. It is the core feature of the effect of responsive programming!

1,Java9 Reactive Stream API

Java 9 provides a set of interfaces that define responsive flow programming. All these interfaces are defined as static internal interfaces in Java util. concurrent. Flow class.

Here are some important roles and concepts in Java responsive programming. Let's briefly understand them first

  • A Publisher is a potential producer of an unlimited number of ordered data elements. It publishes a certain number of data elements to the current subscriber according to the received subscription.

  • Subscribers subscribe to and receive data elements from publishers. After establishing a subscription relationship with the publisher, the publisher sends a subscription token to the Subscriber. The Subscriber can request the publisher to publish the number of data elements according to its processing capacity.

  • A subscription token represents a subscription relationship established between a subscriber and a publisher. When a subscription relationship is established, the publisher passes it to the subscriber. Subscribers use subscription tokens to interact with publishers, such as requesting the number of data elements or unsubscribing.

2. Four interfaces of Java responsive programming

2.1.Subscriber Interface

public static interface Subscriber<T> {
    public void onSubscribe(Subscription subscription);
    public void onNext(T item);
    public void onError(Throwable throwable);
    public void onComplete();
}
  • onSubscribe: called before publishing any Subscription message after the publisher accepts the subscriber's Subscription action. The newly created Subscription token object is passed to the subscriber through this method.
  • onNext: the processing function of the next data item to be processed
  • onError: called when the publisher or subscription encounters an unrecoverable error
  • onComplete: called when no subscriber calls (including the onNext() method) occur.

2.2.Subscription Interface

Subscription token object via subscriber Onsubscribe() method pass

public static interface Subscription {
    public void request(long n);
    public void cancel();
}
  • request(long n) is the key method behind the concept of non blocking back pressure. Subscribers use it to request more than n consumption items. In this way, the subscriber controls how many data it can currently receive.
  • cancel() the subscriber cancels his subscription on his own initiative. After cancellation, no data message will be received.

2.3. Publisher interface

@FunctionalInterface
public static interface Publisher<T> {
    public void subscribe(Subscriber<? super T> subscriber);
}

Call this method to establish the message subscription relationship between Subscriber and Publisher.

2.4.Processor Interface

The Processor can act as both a subscriber and a publisher, transforming elements in the publisher subscriber pipeline. It is used to receive and convert the publisher's data element of type T to data of type R and publish it.

public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> {
}

3. Actual combat cases

Now we need to implement the above four interfaces to complete responsive programming

  • Subscription Interface the subscription token interface usually does not need to be implemented by ourselves. We only need to know the meaning of the request() method and the cancle() method.
  • Publisher Interface, Java 9 has provided us with the implementation of SubmissionPublisher by default. In addition to the method of implementing Publisher Interface, this implementation class provides a method called submit() to complete the sending of message data.
  • Subscriber Interface the Subscriber Interface usually needs to be implemented by ourselves. Because after the data subscription is received, different services have different processing logic.
  • Processor is actually a combination of Publisher Interface and Subscriber Interface. It needs data type conversion and data processing to implement this interface

Code example 1:

package test;

import java.util.concurrent.Flow;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.SubmissionPublisher;

public class FlowDemo {

	public static void main(String[] args) throws Exception {
		
		// 1. Define the publisher. The published data type is Integer
		// Directly use the submission Publisher provided by JDK, which implements the Publisher interface
		SubmissionPublisher<Integer> publisher = new SubmissionPublisher<Integer>();

		// 2. Define subscribers
		Subscriber<Integer> subscriber = new Subscriber<Integer>() {
			
			private Subscription subscription; // Subscription object reference
			
			@Override
			public void onSubscribe(Subscription subscription) {
				// To save the subscription relationship, you need to use it to respond to the publisher
				this.subscription = subscription;
				// Request a data
				this.subscription.request(1);
			}
			
			@Override
			public void onNext(Integer item) {
				// A data is received and processed
				System.out.println("Data received: " + item);
				// After processing the call request, request another data
				this.subscription.request(1);
				// Or if the target has been reached, call cancel to tell the publisher that it will no longer receive data
				// this.subscription.cancel();
			}
			
			@Override
			public void onError(Throwable throwable) {
				// An exception has occurred (for example, an exception occurred while processing data)
				throwable.printStackTrace();
				// Call cancel to tell the publisher that the data is no longer received
				this.subscription.cancel();
			}

			@Override
			public void onComplete() {
				// All data has been processed (the publisher has closed)
				System.out.println("It's done");
			}
		};
		
		// 3. Establish subscription relationship between publisher and subscriber
		publisher.subscribe(subscriber);
		
		// 4. Production data and release
		int data = 111;
		publisher.submit(data);
//		publisher.submit(222);
//		publisher.submit(333);
		
		// 5. When finished, close the publisher
		publisher.close();
		
		// The main thread simulates a delay stop, otherwise it exits without data consumption
		Thread.currentThread().join(1000);
	}
}

Code example 2:

package test;

import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.SubmissionPublisher;

/**
 * To customize the Processor, you need to inherit SubmissionPublisher and implement the Processor interface
 * Enter the source data integer, filter out the data less than 0, and then convert it into a string and publish it
 */
class Myprocessor extends SubmissionPublisher<String> implements Processor<Integer, String> {
	
	private Subscription subscription; // Subscription object reference
	
	@Override
	public void onSubscribe(Subscription subscription) {
		// To save the subscription relationship, you need to use it to respond to the publisher
		this.subscription = subscription;
		// Request a data
		this.subscription.request(1);
	}
	
	@Override
	public void onNext(Integer item) {
		// A data is received and processed
		System.out.println("Data received by processor: " + item);
		// Filter those less than 0 and publish them
		if (item > 0) {
			this.submit("Converted data: " + item);
		}
		// After processing the call request, request another data
		this.subscription.request(1);
		// Or if the target has been reached, call cancel to tell the publisher that it will no longer receive data
		// this.subscription.cancel();
	}
	
	@Override
	public void onError(Throwable throwable) {
		// An exception has occurred (for example, an exception occurred while processing data)
		throwable.printStackTrace();
		// Call cancel to tell the publisher that the data is no longer received
		this.subscription.cancel();
	}

	@Override
	public void onComplete() {
		// All data has been processed (the publisher has closed)
		System.out.println("The processor has finished processing");
	}
}

public class FlowDemo2 {

	public static void main(String[] args) throws Exception {
		
		// 1. Define the publisher. The published data type is Integer
		// Directly use the submission Publisher provided by JDK, which implements the Publisher interface
		SubmissionPublisher<Integer> publisher = new SubmissionPublisher<Integer>();
		
		// 2. Define the processor, filter the data and convert it to String type
		MyProcessor myProcessor = new MyProcessor();
		
		// 3. Establish subscription relationship between publisher and processor
		publisher.subscribe(myProcessor);

		// 4. Define the final subscriber and consume String type data
		Subscriber<String> subscriber = new Subscriber<String>() {
			
			private Subscription subscription; // Subscription object reference
			
			@Override
			public void onSubscribe(Subscription subscription) {
				// To save the subscription relationship, you need to use it to respond to the publisher
				this.subscription = subscription;
				// Request a data
				this.subscription.request(1);
			}
			
			@Override
			public void onNext(String item) {
				// A data is received and processed
				System.out.println("Data received: " + item);
				// After processing the call request, request another data
				this.subscription.request(1);
				// Or if the target has been reached, call cancel to tell the publisher that it will no longer receive data
				// this.subscription.cancel();
			}
			
			@Override
			public void onError(Throwable throwable) {
				// An exception has occurred (for example, an exception occurred while processing data)
				throwable.printStackTrace();
				// Call cancel to tell the publisher that the data is no longer received
				this.subscription.cancel();
			}

			@Override
			public void onComplete() {
				// All data has been processed (the publisher has closed)
				System.out.println("It's done");
			}
		};
		
		// 5. The processor establishes a subscription relationship with the subscriber
		myProcessor.subscribe(subscriber);
		
		// 6. Production data and release
		publisher.submit(111);
		publisher.submit(-111);
		publisher.submit(222);
		
		// 7. When finished, close the publisher
		publisher.close();
		
		// The main thread simulates a delay stop, otherwise it exits without data consumption
		Thread.currentThread().join(1000);
	}
}

4, Spring5 responsive programming / SpringWebFlux

1. Introduction to weblux

Spring WebFlux is a new responsive web framework introduced in Spring Framework 5.0. Unlike Spring MVC, it does not require Servlet API, is completely asynchronous and non blocking, and implements the Reactive Streams specification through the Reactor project.

Spring WebFlux is used to create fully asynchronous and non blocking applications based on the event loop execution model.

(PS: the so-called asynchronous non blocking is for the server, which means that the server can make full use of CPU resources to do more things, which has nothing to do with the client. How should the client request or how to request.)

Reactive Streams is a set of specifications for building high-throughput, low latency applications. The Reactor project is based on the implementation of this set of specifications. It is a completely non blocking foundation and supports back pressure. Spring WebFlux implements a fully asynchronous and non blocking web framework based on Reactor, which is a set of responsive stacks.

[spring webmvc + servlet + Tomcat] imperative and synchronous blocking

[spring weblux + reactor + netty] responsive, asynchronous and non blocking

2. WebFlux application scenario

As mentioned above, Spring WebFlux is an asynchronous and non blocking Web framework, so it is especially suitable for applications in IO intensive services, such as microservice gateway.

PS: IO intensive includes disk IO intensive and network IO intensive. Microservice gateway belongs to network IO intensive. Using asynchronous non blocking programming model can significantly improve the throughput of gateway forwarding to downstream services.

3. WebFlux or Spring MVC?

First of all, you need to be clear: WebFlux is not an alternative to Spring MVC!, Although WebFlux can also be run on Servlet containers (containers above Servlet 3.1 +), WebFlux is mainly used in asynchronous non blocking programming model, and Spring MVC is synchronous blocking. If you currently use a large number of asynchronous schemes in Spring MVC framework, WebFlux is what you want. Otherwise, Using Spring MVC is your first choice.

In the microservice architecture, Spring MVC and WebFlux can be mixed. For example, as already mentioned, we can use WebFlux to implement IO intensive services (such as gateways).

WebFlux or Spring MVC? This is not a problem!

We should not pretend to force for the sake of force and technology for the sake of technology. We should also consider many factors, such as the steep learning curve of turning to non blocking responsive programming, the learning cost of team members and so on.

In a word, select the most appropriate technology in the right scenario.

4. Similarities and differences

From the above figure, we can see the similarities and differences between Spring MVC and Spring WebFlux at a glance:

Similarities:

  • Spring MVC annotations, such as @ Controller, can be used to facilitate the free conversion between the two Web frameworks;
  • You can use tomcat, jetty and undertow servlet containers (Servlet 3.1 +);
  • ...

Note:

  • Because Spring MVC uses synchronous blocking, it is more convenient for developers to write functional code, Debug test, etc. Generally speaking, if Spring MVC can meet the scenarios, try not to use WebFlux;
  • WebFlux uses Netty as the server by default;
  • WebFlux does not support MySql;

5. Actual combat cases

Temporarily

Keywords: Java Lambda stream

Added by cool-palace-ceo on Sun, 16 Jan 2022 16:06:41 +0200