1, Lambda expression
1. Examples of Lambda expressions
@Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("I Love Beijing Tiananmen "); } }; r1.run(); System.out.println("***********************"); Runnable r2 = () -> System.out.println("I Love Beijing Tiananmen "); r2.run(); } @Test public void test2(){ Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; int compare1 = com1.compare(12,21); System.out.println(compare1); System.out.println("***********************"); //Writing method of Lambda expression Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2); int compare2 = com2.compare(32,21); System.out.println(compare2); System.out.println("***********************"); //Method reference Comparator<Integer> com3 = Integer::compare; int compare3 = com3.compare(32,21); System.out.println(compare3); }
2. Use of Lambda expressions
①eg: (o1,o2) -> Integer.compare(o1,o2);
② Format:
->: lambda operator or arrow operator
Left: lambda formal parameter list (in fact, it is the formal parameter list of abstract methods in the interface)
Right: lambda body (actually the method body of the overridden abstract method)
③ Use of Lambda expressions: (six kinds of introduction)
//Syntax format 1: no parameter, no return value @Test public void test1(){ Runnable r1 = new Runnable(){ @Override public void run(){ System.out.println("I Love Beijing Tiananmen "); } }; r1.run(); System.out.println("***********************"); Runnable r2 = () -> { System.out.println("I Love Beijing Tiananmen "); }; r2.run(); } //Syntax format 2: Lambda needs a parameter, but there is no return value @Test public void test2(){ Consumer<String> con = new Consumer<String>(){ @Override public void accept(String s){ System.out.println(s); } }; con.accept("What is the difference between a lie and an oath?"); System.out.println("*******************"); Comsumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("One is that the listener is serious and the other is that the speaker is serious"); } //Syntax format 3: data types can be omitted because they can be inferred by the compiler, which is called "type inference" @Test public void test3(){ Comsumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("One is that the listener is serious and the other is that the speaker is serious"); System.out.println("*******************"); Comsumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("One is that the listener is serious and the other is that the speaker is serious"); ArrayList<String> list = new ArrayList<>();//Type inference int[] arr = {1,2,3};//Type inference } //Syntax format 4: if Lambda only needs one parameter, the parentheses of the parameter can be omitted @Test public void test5(){ Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("One was taken seriously by the listener and the other was taken seriously by the speaker"); System.out.println("*******************"); Comsumer<String> con2 = s -> { System.out.println(s); } con2.accept("It's true to be a person"); } //Syntax format 5: Lambda requires two or more parameters, multiple execution statements, and can have return values @Test public void test6(){ Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12,21)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12,6)); } //Syntax format 6: when there is only one statement in Lambda body, return and braces, if any, can be omitted @Test public void test7(){ Comparator<Integer> com1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(com1.compare(12,6)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2); System.out.println(com2.compare(12,21)); } @Test public void test8(){ Consumer<String> con1 = s -> { System.out.println(s); }; con1.accept("One was taken seriously by the listener and the other was taken seriously by the speaker"); System.out.println("*****************************"); Consumer<String> con2 = s -> System.out.println(s); con2.accept("One was taken seriously by the listener and the other was taken seriously by the speaker"); }
Summary:
Left: the parameter type of lambda parameter list can be omitted (type inference); If the lambda parameter list has only one parameter, its pair () can also be omitted
Right: lambda body should be wrapped with a pair of {}; If the lambda body has only one execution statement (possibly a return statement), omit this pair of {} and return keywords
④ The essence of Lambda expression: as an example of functional interface.
⑤ If only one abstract method is declared in an interface, the interface is called a functional interface. We can use the @ functional interface annotation on an interface to check whether it is a functional interface.
@FunctionalInterface
public interface MyInterface {/ / user defined functional interface
void method1();
}
⑥ Therefore, those previously expressed by anonymous implementation classes can now be written in Lambda expressions.
2, Functional interface
1. Four core functional interfaces built in java
Consumer < T > void accept (T)
Supplier < T > T get()
Function < T > R apply (T)
Predicate < T > Boolean test (T)
@Test public void test1(){ happyTime(500,new Consumer<Double>(){ @Override public void accept(Double aDouble){ System.out.println("Bought a bottle of mineral water at the price of:" + aDouble); } }); System.out.println("********************"); happyTime(400,money -> System.out.println("Bought a bottle of mineral water at the price of:" + money)); } public void happyTime(double money,Comsumer<Double> con){ con.accept(money); } @Test public void test2(){ List<String> list = Arrays.asList("Beijing","Nanjing","Tianjin","Tokyo","the Western Capital","Vladimir Putin"); List<String> filterStrs = filterString(list,new Predicate<String>(){ @Override public boolean test(String s){ return s.contains("Beijing"); } }); System.out.println(filterStrs); System.out.println("***********************"); List<String> filterStrs1 = filterString(list,s -> s.contains("Beijing")); System.out.println(filterStrs1); } //Filter the strings in the collection according to the given rules. This rule is determined by the predict method public List<String> filterString(List<String> list,Predicate<String> pre){ ArrayList<String> filterList = new ArrayList<>(); for(String s:list){ if(pre.test(s)){ filterList.add(s); } } return filterList; }
Summary:
>When to use lambda expressions?
When you need to instantiate a functional interface, you can use lambda expressions.
3, Method reference and constructor reference
1. Method reference
① Usage context: when the operation to be passed to the Lambda body already has an implemented method, you can use the method reference.
② Method reference is essentially a Lambda expression, which is used as an instance of a functional interface. Therefore, method references are also instances of functional interfaces.
③ Use format: class (or object):: method name
④ There are three situations as follows:
Case 1: object:: non static method
Case 2: Class: static method
Case 3: Class: non static method
⑤ Requirements for use of method references:
>The formal parameter list and return value type of the abstract method in the interface are required to be the same as those of the method referenced by the method! (for cases 1 and 2)
//Case 1: object: instance method //Void accept in Consumer //Void println in PrintStream @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("Beijing"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps :: println; con2.accept("beijing"); } //T get() in Supplier //String getName() in Employee @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp :: getName; System.out.println(sup2.get()); }
//Case 2: Class: static method //Int compare in Comparator (t T1, t T2) //Int compare in Integer (t T1, t T2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer :: compare; System.out.println(com2.compare(12,3)); } //R apply in Function (T) //Long round(Double d) in Math @Test public void test4() { Function<Double,Long> func = new Function<Double,Long>(){ @Override public Long apply(Double d){ return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math :: round; System.out.println(func2.apply(12.6)); }
> when the first parameter of the functional interface method is the caller who needs to reference the method, and the second parameter is the parameter (or no parameter) that needs to reference the method: classname:: methodname
//Case 3: Class: instance method //Int compare in Comparator (t T1, t T2) //Int T1 in String compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //Boolean test in BiPredicate (t T1, t T2); //Boolean T1 in String equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } //R apply in Function (T) //String getName() in Employee @Test public void test7() { Employee employee = new Employee(1001,"Jerry",23,6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee :: getName; System.out.println(func2.apply(employee)); }
⑥ Constructor reference
Similar to method reference, the formal parameter list of abstract method of functional interface is consistent with that of constructor. The return value type of the abstract method is the type of the class to which the constructor belongs.
//Constructor reference //T get() in Supplier //Empty parameter constructor of Employee: Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>(){ @Override public Employee get(){ return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //R apply in Function (T) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //R apply (T, t, u, U) in BiFunction @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); }
⑦ Array reference
The array is regarded as a special class, and the writing method is consistent with the constructor reference.
//Array reference //R apply in Function (T) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }
4, Powerful Stream API
1. The difference between Stream and collection
Stream focuses on the operation of data and dealing with CPU
Collections focus on the storage of data and deal with memory
Java 8 provides a set of APIs, which can be used to filter, sort, map, reduce and other operations on the data in memory. This is similar to sql operations on tables in the database.
2. Characteristics of Stream
① Stream itself does not store elements
② Stream does not change the source object. Instead, they return a new stream that holds the result
③ Stream operations are deferred. This means they wait until they need results
3. Stream execution process
① Instantiation of Stream
② A series of intermediate operations (filtering, mapping,...)
③ Terminate operation
4. Explain
① An intermediate operation chain that processes the data of the data source.
② Once the termination operation is performed, the intermediate operation chain is executed and the results are generated. After that, it will not be used again.
5. Instantiation of Stream:
① Method 1 of creating Stream: through collection
@Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees(); //Default stream < E > stream(): returns a sequential stream Stream<Employee> stream = employees.stream(); //Default stream < E > parallelstream(): returns a parallel stream Stream<Employee> parallelStream = employees.parallelStream(); }
② Method 2 of creating Stream: through array
@Test public void test2(){ int[] arr = new int[]{1,2,3,4,5,6}; //Call static < T > stream < T > stream (t [] array) of Arrays class: return a stream IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001,"Tom"); Employee e2 = new Employee(1002,"Jerry"); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); }
③ Three ways to create a Stream: through the of() of the Stream
@Test public void test3(){ Stream<Integer> stream = Stream.of(1,2,3,4,5,6); }
④ Method 4 of creating Stream: create infinite Stream
@Test public void test4(){ //iteration //public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f) //Traverse the first 10 even numbers Stream.iterate(0,t -> t+2).limit(10).forEach(System.out :: println); //generate //public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math :: random).limit(10).forEach(System.out :: println); }
6. Intermediate operation of Stream
① Screening and slicing
@Test public void test1(){ List<Employee> list = EmployeeData.getEmployees(); //Filter (predict P): receive Lambda and exclude some elements from the stream Stream<Employee> stream = list.stream(); //Exercise: query employee information with salary greater than 7000 in employee table stream.filter(e -> e.getSalary() > 7000).forEach(System.out :: println); System.out.println(); //limit(n): truncate the stream so that its elements do not exceed the given number list.stream().limit(3).forEach(System.out :: println); System.out.println(); //skip(n): skip elements and return a stream that throws away the first n elements. If there are less than n elements in the stream, an empty stream is returned. Complementary to limit(n) list.stream().skip(3).forEaach(System.out :: println); System.out.println(); //distinct(): filter and remove duplicate elements through hashCode() and equals() of the elements generated by the stream list.stream().distinct().forEach(System.out :: println); }
② Mapping
public class StreamAPITest{ @Test public void test2(){ //map(Function f): receive a function as a parameter to convert elements into other forms or extract information. The function will be applied to each element and mapped to a new element. List<String> list = Arrays.asList("aa","bb","cc","dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out :: println); //Exercise 1: get the name of an employee whose name length is greater than 3. List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee :: getName); namesStream.filter(name -> name.length() > 3).forEach(System.out :: println); System.out.println(); //Exercise 2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest :: fromStringToStream); streamStream.forEach(s -> { s.forEach(System.out :: println); }); System.out.println(); //flatMap(Function f): take a function as a parameter, change each value in the stream into another stream, and then connect all streams into one stream Stream<Character> characterStream = list.stream().flatMap(StreamAPITest :: fromStringToStream); characterStream.forEach(System.out :: println); } //Converts a set composed of multiple characters in a string into an instance of the corresponding Stream public static Stream<Character> fromStringToStream(String str){ //aa ArrayList<Character> list = new ArrayList<>(); for(Character c:str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test3(){ ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6); // list1.add(list2); list1.addAll(list2); System.out.println(list1); } }
③ Sort
@Test public void test4(){ //sorted(): natural sorting List<Integer> list = Arrays.asList(12,43,65,34,87,0,-98,7); list.stream().sorted().forEach(System.out :: println); //Throw exception. Reason: Employee does not implement Comparable interface //List<Employee> employees = EmployeeData.getEmployees(); //employees.stream().sorted().forEach(System.out :: println); //sorted(Comparator com): customized sorting List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted((e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if(ageValue != 0){ return ageValue; }else{ return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out :: println); }
7. Termination of Stream
① Match and find
@Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees(); //allMatch(Predicate p): check whether all elements are matched //Exercise: are all employees older than 18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch); //Anymatch (predict P): checks whether at least one element is matched //Exercise: is there an employee whose salary is greater than 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch); //Nonematch (predict P): check whether there are no matching elements //Exercise: is there an employee surnamed "Lei" boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("thunder")); System.out.println(noneMatch); //findFirst(): returns the first element Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee); //findAny(): returns any element in the current stream Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); }
@Test public void test2(){ List<Employee> employees = EmployeeData.getEmployees(); //count: returns the total number of elements in the stream long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count); //max(Comparator c): returns the maximum value in the stream //Exercise: return to the highest salary: Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double :: compare); System.out.println(maxSalary); //min(Comparator c): returns the minimum value in the stream //Exercise: return to the minimum wage employee Optional<Employee> employee = employees.stream().min((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())); System.out.println(employee); System.out.println(); //forEach(Consumer c): internal iteration employees.stream().forEach(System.out :: println); //Traversal using collections employees.forEach(System.out :: println); }
② Reduction
@Test public void test3(){ //Reduce (T identity, binary operator): you can combine the elements in the stream repeatedly to get a value. Return T //Exercise 1: calculate the sum of natural numbers 1-10 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0,Integer :: sum); System.out.println(sum); //Reduce (binary operator): you can combine the elements in the flow repeatedly to get a value. Return to optional < T > //Exercise 2: calculate the total salary of all employees in the company List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee :: getSalary); //Optional<Double> sumMoney = salaryStream.reduce(Double :: sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); }
③ Collect
@Test public void test4(){ //collect(Collector c): convert a Stream to another form. Receive the implementation of a collector interface, which is used to summarize the elements in the Stream //Exercise 1: find employees whose salary is greater than 6000, and the result is returned as a List or Set List<Employee> employees = EmployeeData.getEmployees(); //List < T > tolist(): collect the elements in the stream into a list List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out :: println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out :: println); }
Collector s need to use Collectors to provide instances:
5, Optional class
1. The optional < T > class (java.util.Optional) is a container class that can store the value of type T, representing the existence of this value. Or just save null, indicating that the value does not exist.
2. Optional class: created to avoid null pointer exceptions in programs.
3. Common methods:
Optional. Of (t): create an optional instance. t must be non empty
Optional.empty(): create an empty optional instance
Optional. Ofnullable (T): t can be null
@Test public void test1(){ //empty(): value = null inside the created Optional object Optional<Object> op1 = Optional.empty(); if(!op1.isPresent()){ //Does the Optional encapsulated data contain data System.out.println("Data is empty"); } System.out.println(op1); System.out.println(op1.isPresent()); //If the data value encapsulated by option is empty, get() will report an error. Otherwise, value is returned when value is not empty. System.out.println(op1.get()); }
@Test public void test2(){ String str = "hello"; //str = null; //Of (T): encapsulates data t to generate Optional objects. It is required that t is not empty, otherwise an error will be reported. Optional<String> op1 = Optional.of(str); //get(): usually used with the of() method. Used to obtain the internally encapsulated data value String str1 = op1.get(); System.out.println(str1); }
@Test public void test3(){ String str = "beijing"; str = null; //ofNullable(T t): the value that encapsulates the data t assigned to the internal part of Optional. Don't ask t to be non empty Optional<String> op1 = Optional.ofNullable(str); //orElse(T t1): if the value inside Optional is not empty, this value will be returned; If value is empty, t1 is returned. String str2 = op1.orElse("shanghai"); System.out.println(str2); }