Learning notes on new features of JAVA 8

Learning notes on new features of JAVA 8

Lambda expression

1, Introduction to Lambda expressions

What is Lambda?

Lambda is a new feature added to JAVA 8. To put it bluntly, lambda is an anonymous function

Why Lambda

Using Lambda expressions, you can implement the methods of an interface very concisely

Lambda's requirements for interfaces

Although some interfaces can be simply implemented with Lambda expressions, not all interfaces can be implemented with Lambda expressions. It is required that only one abstract method defined in the interface must be implemented

Lambda expressions require functional interface support

Functional interface: an interface with only one abstract method, which becomes a functional interface. You can use the annotation @ FunctionalInterface to modify it. The function of the annotation is to check whether the interface is a functional interface

stay JAVA8 In, a new feature is added to the interface: default
 have access to default Modify the interface method. The modified method can be implemented by default in the interface

@FunctionalInterface

Modifies a functional interface. There is only one abstract method in the interface

2, Basic grammar of Lambda

Basic syntax of Lambda expression: JAVA 8 introduces a new operator "- >", which becomes an arrow operator or Lambda operator
The arrow operator splits the Lambda expression into two parts:
On the left is the parameter list of Lambda expression
On the right is the function to be performed in the Lambda expression, that is, the Lambda body

grammar

/**
 * Syntax format 1: no parameter, no return value
 *          () -> System.out.println("Hello Lambda!");
 */
@Test
public void test1() {
    // Before JDK 1.8, when global parameters are passed into local internal classes (anonymous internal classes), they need to be modified with final. After JDK 1.8, they will be modified with final by default, which can be omitted 
    int num = 0;

    // Original usage
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello Lambda!" + num);
        }
    };
    r.run();
    System.out.println("----------------------------------");
    // Lambda expression usage
    Runnable r1 = () -> System.out.println("Hello Lambda!" + num);
    r1.run();
}

/**
 * Syntax format 2: there is a parameter and no return value
 *         (x) -> System.out.println(x); 
 */
@Test
public void test2() {
    Consumer<String> con = (x) -> System.out.println(x);
    con.accept("Hello Lambda!");

    Consumer<String> con1 = System.out::println;
    con1.accept("Hello Lambda!!!");
}

/**
 * Syntax format 3: if there is only one parameter, parentheses can be omitted
 *          x -> System.out.println(x);
 */
@Test
public void test3() {
    Consumer<String> con = x -> System.out.println(x);
    con.accept("Hello Lambda!");
}

/**
 * Syntax format 4: there are two or more parameters, and there are multiple statements in the Lambda body
 *          (x, y) - {
 *              System.out.println(x+"->"+y)
 *              return Integer.compare(x, y);
 *          }
 */
@Test
public void test4() {
    Comparator<Integer> com = (x, y) -> {
        System.out.println(x + y);
        return Integer.compare(x, y);
    };
}

/**
 * Syntax format 5: if there are two or more parameters and there is only one statement in the Lambda body, return and braces {} can be omitted
 *          (x, y) -> Integer.compare(x, y);
 *          Integer::compare;
 */
@Test
public void test5() {
    // First format
    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    // Second format
    Comparator<Integer> com1 = Integer::compare;
}

/**
 * Syntax format 6: the data type of the parameter list of Lambda expression can be omitted, because the JVM compiler will infer according to the context, that is, "type inference"
 *			(x, y) -> Integer.compare(x, y);
 *			(Integer x, Integer y) -> Integer.compare(x, y);
 */

summary

Upper couplet: left and right meet a bracket, and lower couplet: infer the type on the left. Horizontal batch: save if you can

3, Four core built-in functional interfaces of Java 8

After learning the four built-in interfaces, you can use most of Lambda's methods!!!

Interfacemethoddescribe
Consumervoid accept(T t)Consumer interface
SupplierT get()Supply type interface
Function<T, R>R apply(T t)Functional interface
Predicateboolean test(T t)Assertive interface

Consumer: consumer interface

It is used to consume some services. Examples are as follows

// Consumer < T >: consumer interface       
@Test
public void test() {
    happy(10000, x -> System.out.println("Xiaoming spends 1000 yuan today"));
}

public void happy(double money, Consumer<Double> consumer) {
    consumer.accept(money);
}

Supplier: supply type interface

Used to provide services, examples are as follows

// Supplier < T >: supply type interface   
@Test
public void test() {
    List<Integer> purchase = Purchase(10, () -> (int) (Math.random() * 100));
    purchase.forEach(System.out::println);
}

// Requirement: generate a specified number of integers and put them into the interface
public List<Integer> Purchase(int num, Supplier<Integer> supplier) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < num; i++) {
        list.add(supplier.get());
    }
    return list;
}

Function < T, R >: functional interface

It is used to process data and return some of the same or different data types. Examples are as follows

// Function < T, R >: functional interface 
@Test
public void test3() {
    System.out.println(fun("biandekaixin", k -> k.toUpperCase(Locale.ROOT).substring(2, 4)));

    System.out.println(fun1(new Employee(1, "Zhang San", 18, 6000), Employee::getName));
}

public String fun(String str, Function<String, String> function) {
    return function.apply(str);
}

public String fun1(Employee employee, Function<Employee, String> function) {
    return function.apply(employee);
}

Predicate: predicate interface

It is used to process the data that meets the conditions. For example, elements with filter length greater than 3 are placed in a new array. Examples are as follows:

// Predicate < T >: predicate interface
@Test
public void test4() {
    List<String> list = new ArrayList<>();
    list.add("www");
    list.add("Hello");
    list.add("Lambda");
    list.add("ok");
    list.add("cccc");
    List<String> str = filterStr(list, k -> k.length() > 3);
    str.forEach(System.out::println);
}

// Requirement: put the string meeting the requirement into the collection
public List<String> filterStr(List<String> strs, Predicate<String> predicate) {
    List<String> list = new ArrayList<>();
    for (String str : strs) {
        if (predicate.test(str)) {
            list.add(str);
        }
    }
    return list;
}

Extended sub interface with built-in four core functional interfaces

Interfacemethoddescribe
BiFunction <T, U, R>R apply (T t, U u)Operate on the passed in parameters and return the R-type processing results
UnaryOperator T apply (T t)Perform unary operation on the parameter and return the result of type T
BinaryOperator T apply (T t1, T t2)Binary operation is performed on the parameter to return the result of type T
BiConsumer <T, U>void accept (T t, U u)Operate on parameters t and u without returning results
ToIntFunction int applyAsInt (T t)Strong conversion of the passed in parameter to int type return
ToLongFunction long applyAsLong (T t)Strong conversion of the passed in parameter to long type return
ToDoubleFunction double applyAsDouble (T t)Force the passed in parameter into double type and return
IntFunction R apply(int param)After processing, an int type is forcibly converted to R type and returned
LongFunction R apply(long param)After processing, a long type is forcibly converted to R type and returned
DoubleFunction R apply(double param)After processing, a double parameter is forcibly converted to R type and returned

4, Method reference

Method reference: if Lambda The content in the body has methods that have been implemented. We can use "method reference"
 				(It can be understood that a method reference is Lambda Another form of expression)

 There are three main syntax formats:
 
		 Object name::Instance method name

		 class::Static method name

		 class::Instance method name
		 
be careful:
   1,Lambda The parameter list and return value type of the calling method are consistent with the function list and the return value type of the abstract method in the functional interface..
   2,if Lambda This parameter can be used when the first parameter in the parameter list is the caller of the instance method and the second parameter is the parameter of the instance method ClassName::method.

Object name:: instance method name

// Object name:: instance method name
@Test
public void test1() {
    PrintStream out = System.out;
    
    Consumer<String> con = (x) -> out.println(x);
   
    Consumer<String> con2 = out::println;
    
    Consumer<String> con3 = System.out::println;
    con3.accept("ccc");
}

@Test
public void test2() {
    Employee emp = new Employee(1, "zs", 18, 2000);
    
    Supplier<String> s = () -> emp.getName();
    System.out.println(s.get());

    Supplier<Integer> s1 = emp::getAge;
    System.out.println(s1.get());
}

Class name:: static method name

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

Class name:: instance method name

// Class:: instance method name
@Test
public void test4() {
    BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    BiPredicate<String, String> bp1 = String::equals;
}

5, Constructor reference

Format:
	ClassName :: new
 be careful:
	The parameter list of the constructor to be called should be consistent with the parameter list of the abstract method in the functional interface!

ClassName :: new

// Entity class
@Data
@Accessors(chain = true)
public class Employee {
    /**
     * id
     */
    private Integer id;
    /**
     * Name of
     */
    private String name;
    /**
     * Age
     */
    private Integer age;
    /**
     * wages
     */
    private Integer salary;

    public Employee(Integer id, String name, Integer age, Integer salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Employee(Integer id, Integer age) {
        this.id = id;
        this.age = age;
    }

    public Employee(Integer id) {
        this.id = id;
    }

    public Employee() {

    }
}
// Constructor reference
@Test
public void test5() {
    Supplier<Employee> s = () -> new Employee();
    System.out.println(s.get());

    Supplier<Employee> s1 = Employee::new;
    System.out.println(s1.get());
}

@Test
public void test6() {
    Function<Integer, Employee> fun = Employee::new;
    System.out.println(fun.apply(15));

    BiFunction<Integer, Integer, Employee> bf = Employee::new;
    System.out.println(bf.apply(11, 18));
}

6, Array reference

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

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

Stream API

1, Introduction to Stream API

What is Stream?

A Stream is a queue of elements from a data source and supports aggregation operations

  1. Elements are specific types of objects that form a queue. Stream in Java does not store elements, but calculates on demand.
  2. The source of the data stream. It can be set, array, I/O channel, generator, etc.
  3. Aggregation operations are similar to SQL statements, such as filter, map, reduce, find, match, sorted, etc.

2, Three operation steps of Stream

Stream There are three steps:
 
	1,establish Stream

	2,Intermediate operation

	3,Abort operation (terminal operation)

Create Stream

// There are four ways to create a Stream
@Test
public void test1() {
    // Stream() (serial stream) and parallelStream() (parallel stream) provided through the Collection series Collection
    List<String> list = new ArrayList<>();
    // First: serial stream
    Stream<String> stream1 = list.stream();
    // Second: parallel flow
    Stream<String> stream2 = list.parallelStream();

    // Get the array stream through the static method stream() in Arrays
    Employee[] arr = new Employee[]{};
    Stream<Employee> stream3 = Arrays.stream(arr);

    // You can pass variable parameters, arrays and collections through the of() method in the Stream class
    Stream<String> stream4 = Stream.of("zs", "ls", "ww");

    // Create infinite flow
    // The first method: the first parameter of the iteration is from which to start, and the second is a word interface of the functional interface Function
    Stream<Integer> stream5 = Stream.iterate(0, x -> x + 2);
    stream5.limit(10)
        .forEach(System.out::println);
    // The second kind: the production wears a functional interface and a consumer interface
    Stream<Double> stream6 = Stream.generate(() -> Math.random());
    stream6.limit(5)
        .forEach(System.out::println);
}

Intermediate operation

Note: multiple intermediate operations can be connected to form a pipeline. Unless the termination operation is triggered on the pipeline, the intermediate operation will not perform any processing! When the operation is terminated, it is processed all at once, which is called * * "lazy evaluation" * *. The intermediate operation of Stream will not have any result data output.

As a whole, the intermediate operations of Stream can be divided into filtering and slicing, mapping and sorting.

// Data sources used for the following operations
List<Employee> employees = Arrays.asList(
            new Employee(1, "Zhang San", 18, 2500),
            new Employee(2, "Li Si", 25, 6500),
            new Employee(3, "Wang Wu", 38, 6000),
            new Employee(4, "Zhao Liu", 45, 8500),
            new Employee(5, "pseudo-ginseng", 25, 1500),
    		new Employee(5, "pseudo-ginseng", 25, 1500),
            new Employee(5, "pseudo-ginseng", 25, 1500)
    );
Screening and slicing
methoddescribe
filter(Predicate p)Receive a Lambda expression to exclude some elements from the stream
distinct()Filter to remove duplicate elements through hashCode() and equals() of the elements generated by the flow
limit(long maxSize)Truncate the stream so that its elements do not exceed the given number
skip(long 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)
// filter filtering
// Internal iteration: the iteration operation is completed by the Stream API
@Test
public void test1() {
    // Intermediate operations do not perform anyone's operations
    Stream<Employee> stream = employees.stream()
        .filter(emp -> emp.getAge() > 35);
    // Abort all operations at once, becoming lazy evaluation
    stream.forEach(System.out::println);
	/**
	 *Output: Employee(id=3, name = Wang Wu, age=38, salary=6000)
	 *	   Employee(id=4, name=Zhao Liu (age=45, salary=8500)
	 */
}

// External iteration
@Test
public void test2() {
    Iterator<Employee> iterator = employees.iterator();

    if (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

// limit truncated flow  
// As long as we find the data we need, such as limit(2), after finding two data, we won't continue the internal iteration. If the limit number is satisfied, it will be short circuited and will not be executed
@Test
public void test3() {
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .forEach(System.out::println);
    /** 
     *Output: Employee(id=2, name = Li Si, age=25, salary=6500)
	 *	   Employee(id=3, name=Wang Wu (age=38, salary=6000)
     *     Employee(id=4, name=Zhao Liu (age=45, salary=8500)
	 */
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .limit(2)
        .forEach(System.out::println);
    /** 
     *Output: Employee(id=2, name = Li Si, age=25, salary=6500)
	 *	   Employee(id=3, name=Wang Wu (age=38, salary=6000)
	 */
}

// Skip skip element
@Test
public void test4() {
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .forEach(System.out::println);
	/** 
     *Output: Employee(id=2, name = Li Si, age=25, salary=6500)
	 *	   Employee(id=3, name=Wang Wu (age=38, salary=6000)
     *     Employee(id=4, name=Zhao Liu (age=45, salary=8500)
	 */
    employees.stream()
        .filter(e -> e.getSalary() > 3500)
        .skip(1)
        .forEach(System.out::println);
    /** 
     *Output: Employee(id=4, name = Zhao Liu, age=45, salary=8500)
	 */
}

// distinct de duplication
// Filter through hashCode() and equals()
@Test
public void test5() {
    employees.stream()
        .filter(e -> e.getSalary() < 3500)
        .forEach(System.out::println);
    /**
     * Output: Employee(id=1, name = Zhang San, age=18, salary=2500)
     *      Employee(id=5, name=Tianqi (age=25, salary=1500)
     *      Employee(id=5, name=Tianqi (age=25, salary=1500)
     *      Employee(id=5, name=Tianqi (age=25, salary=1500)
     */
    employees.stream()
        .filter(e -> e.getSalary() < 3500)
        .distinct()
        .forEach(System.out::println);
    /**
     *Output: Employee(id=1, name = Zhang San, age=18, salary=2500)
     *     Employee(id=5, name=Tianqi (age=25, salary=1500)
     */
}
mapping
methoddescribe
map(Function f)Receive a function as an argument, which is applied to each element and mapped to a new element.
mapToDouble(ToDoubleFunction f)Receive a function as an argument, which will be applied to each element to generate a new DoubleStream.
mapToInt(ToIntFunction f)Receive a function as a parameter, which will be applied to each element to generate a new IntStream.
mapToLong(ToLongFunction f)Receive a function as an argument, which will be applied to each element to generate a new LongStream
flatMap(Function f)Take a function as a parameter, replace each value in the stream with another stream, and then connect all streams into one stream
// Map mapping flow is to apply the functional interface in map (function < T > fun) to each element
@Test
public void test6() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    list.stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);
    /**
     * Output:
     *      AA
     *      BB
     *      CC
     *      DD
     */

    employees.stream()
        .map(Employee::getName)
        .distinct()
        .forEach(System.out::println);
    /**
     * Output:
     *      Zhang San
     *      Li Si
     *      Wang Wu
     *      Zhao Liu
     *      pseudo-ginseng
     */
}

// Maptodouble (todoublefunction f), maptoint (tointfunction f) and maptolong (tolongfunction f) are not examples

// flatMap() takes a stream as a parameter, converts each value in one stream into another stream, and connects all streams into one stream. The relationship between map() and flatMap() is similar to the relationship between methods in list and add() and addAll()
@Test
public void test7() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    // map()
    list.stream()
        .map(TestStreamAPI::filterCharacter) // There's this {[a,a],[b,b],[c,c],[d,d]}
        .forEach(k -> k.forEach(System.out::println));
    // flatMap()
    list.stream()
        .flatMap(TestStreamAPI::filterCharacter) // Inside the village is this {a,a,b,b,c,c,d,d}
        .forEach(System.out::println);

}

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

// The difference between add() and addAll()
@Test
public void test8() {
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    List list2 = new ArrayList();
    
    list2.add(11);
    list2.add(22);
    list2.add(list);
    list2.forEach(System.out::println);
    /**
     *Output:
     *	11
     *	22
     *	[aa, bb, cc, dd]
     */
    list2.addAll(list);
    list2.forEach(System.out::println);
    /**
     *Output:
     *	11
     *	22
     *	aa
     *	bb
     *	cc
     *	dd
     */
}
sort
methoddescribe
sorted()Generate a new stream, which is sorted in natural order
sorted(Comparator comp)Generates a new stream, sorted in comparator order
// sorted()
 @Test
public void test9() {
    // Natural sorting
    List<String> list = Arrays.asList("a", "ccc", "bbb", "dd");
    list.stream()
        .sorted()
        .forEach(System.out::println);
    /**
     *Output:
     *	a
     *	bbb
     *	ccc
     *	dd
     */
    // Comparator sort
    employees.stream()
        .distinct()
        .forEach(System.out::println);
	/**
     *Output:
     *	Employee(id=1, name=Zhang San (age=18, salary=2500)
	 *	Employee(id=2, name=Li Si, age=25, salary=6500)
	 *	Employee(id=3, name=Wang Wu (age=38, salary=6000)
	 *	Employee(id=4, name=Zhao Liu (age=45, salary=8500)
	 *	Employee(id=5, name=Tianqi (age=25, salary=1500)
     */
    employees.stream()
        .sorted((e1, e2) -> {
            if (e1.getAge() == e2.getAge()) {
                return e1.getName().compareTo(e2.getName());
            } else {
                return Integer.compare(e1.getAge(), e2.getAge());
            }
        })
        .distinct()
        .forEach(System.out::println);
    /**
     *Output:
     *	Employee(id=1, name=Zhang San (age=18, salary=2500)
	 *	Employee(id=2, name=Li Si, age=25, salary=6500)
	 *	Employee(id=5, name=Tianqi (age=25, salary=1500)
	 *	Employee(id=3, name=Wang Wu (age=38, salary=6000)
	 *	Employee(id=4, name=Zhao Liu (age=45, salary=8500)
     */
}

Terminate operation

The terminal operation generates results from the pipeline of the stream. The result can be any value that is not a stream, such as List, Integer, Double, String, etc., or even void.

In Java 8, the termination operation of Stream can be divided into: find and match, specification and collection.

// Data source used
List<Employee> employees = Arrays.asList(
            new Employee(1, "Zhang San", 18, 2500, Employee.Status.FREE),
            new Employee(2, "Li Si", 25, 6500, Employee.Status.BUSY),
            new Employee(3, "Wang Wu", 38, 6000, Employee.Status.VACATION),
            new Employee(4, "Zhao Liu", 45, 8500, Employee.Status.BUSY),
            new Employee(5, "pseudo-ginseng", 25, 1500, Employee.Status.VACATION)
    );
Find and match
methoddescribe
allMatch(Predicate p)Check that all elements match
anyMatch(Predicate p)Check to see if at least one element matches
noneMatch(Predicate p)Check that no elements match
findFirst()Returns the first element
findAny()Returns any element in the current stream
count()Returns the total number of elements in the stream
max(Comparator c)Returns the maximum value in the stream
min(Comparator c)Returns the minimum value in the stream
forEach(Consumer c)Internal iteration (using the Collection interface requires users to do iterations, which is called external iteration. On the contrary, the Stream API uses internal iteration)
@Test
public void test1() {
    // allMatch() checks whether all elements match. All elements are BUSY AND true. Otherwise, it is false, similar to AND
    boolean allMatch = employees.stream()
        .allMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(allMatch); // false
    // anyMatch() checks that at least one match of the element contains this BUSY. Otherwise, it is false, similar to: OR
    boolean anyMatch = employees.stream()
        .anyMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(anyMatch); // true
    // noneMatch() checks that the element does not match. This BUSY is false, otherwise it is true, similar to:! wrong
    boolean noneMatch = employees.stream()
        .noneMatch(x -> x.getStatus().equals(Employee.Status.BUSY));
    System.out.println(noneMatch); // flase
    // The first element returned by findFirst() is the optional < T > container class, which will be supplemented later
    Optional<Employee> findFirst = employees.stream()
        .sorted(Comparator.comparingInt(Employee::getSalary))
        .findFirst();
    System.out.println(findFirst.get()); // Employee(id=5, name = Tianqi, age=25, salary=1500, status=VACATION)
    // findAny() returns any element, which is an optional < T > container class, which will be supplemented later in the parallel stream of parallelStream()
    Optional<Employee> findAny = employees.parallelStream()
        .filter(k -> k.getStatus().equals(Employee.Status.BUSY))
        .findAny();
    System.out.println(findAny.get()); // Employee(id=2, name = Li Si, age=25, salary=6500, status=BUSY)
}

@Test
public void test2() {
    // count() returns the total number of elements in the stream
    long count = employees.stream()
        .count();
    System.out.println(count);
    // max() returns the maximum value in the stream
    Optional<Employee> max = employees.stream()
        .max(Comparator.comparingInt(Employee::getSalary));
    System.out.println(max.get());
    // min() returns the minimum value in the stream
    Optional<Integer> min = employees.stream()
        .map(Employee::getSalary)
        .min(Integer::compare);
    System.out.println(min.get());
}

// forEach doesn't need to be added
Statute
methoddescribe
reduce(T iden, BinaryOperator b)You can combine the elements in the flow repeatedly to get a value. Return T
reduce(BinaryOperator b)You can combine the elements in the flow repeatedly to get a value. Return to Optional
@Test
public void test3() {
    // reduce(T iden, BinaryOperator b)/reduce(BinaryOperator b) can combine the elements in the stream repeatedly to obtain a value. Return T / optional < T >
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    Integer reduce = list.stream() // Integer returns T because the first parameter is the starting value and it will not be empty
        .reduce(0, Integer::sum);
    System.out.println(reduce); // 45

    Optional<Integer> optional = employees.stream()
        .map(Employee::getSalary)  // Optional is returned. Because there is no starting value, it may be empty, so optional is returned
        .reduce(Integer::sum);
    System.out.println(optional.get());
}
collect
methoddescribe
collect(Collector c)Convert the Stream to another form. Receive the implementation of a Collector interface, which is used to summarize the elements in the Stream
How do I collect Stream streams?
methodReturn typeeffect
toListListCollect the elements in the stream into a List
toSetSetCollect the elements in the stream into a Set
toCollectionCollectionCollect the elements in the stream into the created collection
countingLongCount the number of elements in the flow
summingIntIntegerSumming integer attributes of elements in a stream
averagingIntDoubleCalculates the average value of the Integer attribute of the element in the flow
summarizingIntIntSummaryStatisticsCollect statistics for the Integer attribute in the stream. E.g. average value
joiningStringEach string in the connection stream
maxByOptionalSelect the maximum value according to the comparator
minByOptionalSelect the minimum value according to the comparator
reducingType of reductionStarting from an initial value as an accumulator, BinaryOperator is combined with the elements in the stream one by one to reduce it to a single value
collectingAndThenThe type returned by the conversion functionWrap another collector and convert its result to a function
groupingByMap<K, List>According to an attribute value, the attribute is K and the result is V
partitioningByMap<Boolean, List>Partition according to true or false

The use examples corresponding to each method are shown in the following table.

methodUse example
toListList employees= list.stream().collect(Collectors.toList());
toSetSet employees= list.stream().collect(Collectors.toSet());
toCollectionCollection employees=list.stream().collect(Collectors.toCollection(ArrayList::new));
countinglong count = list.stream().collect(Collectors.counting());
summingIntint total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingIntdouble avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary))
summarizingIntIntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
CollectorsString str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxByOptionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minByOptional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducingint total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThenint how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingByMap<Emp.Status, List> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus));
partitioningByMap<Boolean,List>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));
// Collection
@Test
public void test4() {
    // toList
    List<String> list = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toList());
    list.forEach(System.out::println);
    // toSet
    Set<String> set = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toSet());
    set.forEach(System.out::println);
    // toCollection
    LinkedList<String> linkedList = employees.stream()
        .map(Employee::getName)
        .collect(Collectors.toCollection(LinkedList::new)); // In the latter, the type you want is new, LinkedList, HashSet, etc
    linkedList.forEach(System.out::println);
}

@Test
public void test5() {
    // total
    Long count = employees.stream()
        .collect(Collectors.counting());
    System.out.println(count);
    // average
    Double aDouble = employees.stream()
        .collect(Collectors.averagingInt(Employee::getSalary));
    System.out.println(aDouble);
    // the sum
    Integer sum = employees.stream()
        .collect(Collectors.summingInt(Employee::getSalary));
    System.out.println(sum);
    // Maximum
    Optional<Employee> max = employees.stream()
        .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary())));
    System.out.println(max.get());
    // minimum value
    Optional<Integer> min = employees.stream()
        .map(Employee::getSalary)
        .collect(Collectors.minBy((x, y) -> Integer.compare(x, y)));
    System.out.println(min.get());
}

// grouping
@Test
public void test6() {
    Map<Employee.Status, List<Employee>> groupingBy = employees.stream()
        .collect(Collectors.groupingBy(Employee::getStatus));
    groupingBy.forEach((k, v) -> System.out.println(k + "--->" + v));
    /**
     * Output:
     *      VACATION--->[Employee(id=3, name=Wang Wu, age=38, salary=6000, status=VACATION), Employee(id=5, name = Tianqi, age=25, 		 *		 salary=1500, status=VACATION), Employee(id=5, name = Tianqi, age=25, salary=1500, status=VACATION)]
     *
     *      BUSY--->[Employee(id=2, name=Li Si, age=25, salary=6500, status=BUSY), Employee(id=4, name = Zhao Liu, age=45, 				 *		 salary=8500, status=BUSY)]
     *
     *      FREE--->[Employee(id=1, name=Zhang San, age=18, salary=2500, status=FREE)]
     */
}

// Multi column grouping
@Test
public void test7() {
    Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream()
        .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {
            if (e.getAge() < 30) {
                return "youth";
            } else {
                return "Prime of life";
            }
        })));
    collect.forEach((k, v) -> System.out.println(k + "--->" + v));
    /**
     * Output:
     *      BUSY--->{Youth = [Employee(id=2, name = Li Si, age=25, salary=6500, status=BUSY)], adult = [Employee(id=4, name = Zhao Liu, 			 *		 age=45, salary=8500, status=BUSY)]}
     *
     *      FREE--->{Youth = [Employee(id=1, name = Zhang San, age=18, salary=2500, status=FREE)]}
     *
     *      VACATION--->{Youth = [Employee(id=5, name = Tianqi, age=25, salary=1500, status=VACATION), Employee(id=5, name = Tianqi, 			 *		 age=25, salary=1500, status=VACATION)], adult = [Employee(id=3, name = Wang Wu, age=38, salary=6000, status=VACATION)]}
     */
}

// Slice / partition
@Test
public void test8() {
    Map<Boolean, List<Employee>> partitioningBy = employees.stream()
        .distinct()
        .collect(Collectors.partitioningBy(e -> e.getSalary() > 6000));
    partitioningBy.forEach((k, v) -> System.out.println(k + "---" + v));
    /**
     * Output:
     *      false---[Employee(id=1, name=Zhang San, age=18, salary=2500, status=FREE), Employee(id=3, name = Wang Wu, age=38, salary=6000, 	   *	   status=VACATION),Employee(id=5, name = Tianqi, age=25, salary=1500, status=VACATION)]
     *
     *      true---[Employee(id=2, name=Li Si, age=25, salary=6500, status=BUSY), Employee(id=4, name = Zhao Liu, age=45, salary=8500, 	 *		 status=BUSY)]
     */
}

@Test
public void test9() {
    DoubleSummaryStatistics dds = employees.stream()
        .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dds.getMax());
    System.out.println(dds.getAverage());
    System.out.println(dds.getCount());
    System.out.println(dds.getMin());
    System.out.println(dds.getSum());
}

// After obtaining the data, it can be used for splicing
@Test
public void test10() {
    String names1 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining());
    System.out.println(names1); // Zhang San Li Si Wang Wu Zhao Liu Tian Qi
    String names2 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining(","));
    System.out.println(names2); // Zhang San, Li Si, Wang Wu, Zhao Liu, Tian Qi
    String names3 = employees.stream()
        .map(Employee::getName)
        .distinct()
        .collect(Collectors.joining(",", "==", "=="));
    System.out.println(names3); // ==Zhang San, Li Si, Wang Wu, Zhao Liu, Tian Qi==
}

3, Parallel stream and serial stream

The bottom layer of Java 8 is the ForkJoin framework, which is infinitely split and finally combined into one. Generally speaking, it is faster than serial flow

/**
 * @author G
 */
public class ForkJoinCalculate extends RecursiveTask<Long> {

    private Long start;
    private Long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;
        if (length <= THRESHOLD) {
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            left.fork();
            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();
            return left.join() + right.join();
        }
    }
}


public class TestForkJoin {

    /**
     * ForkJoin frame
     */
    @Test
    public void test1() {

        Instant start = Instant.now();

        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000000);

        Long sum = pool.invoke(task);

        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("Time consuming:" + Duration.between(start, end).toMillis()); //133
    }

    /**
     * Ordinary for
     */
    @Test
    public void test2() {
        Instant start = Instant.now();
        Long sum = 0L;
        for (long i = 0; i <= 100000000L; i++) {
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("Time consuming:" + Duration.between(start, end).toMillis()); //958
    }

    /**
     * java8 Parallel stream
     */
    @Test
    public void test3() {
        Instant start = Instant.now();
        long sum = LongStream.rangeClosed(0, 100000000L)
                .parallel()
                .reduce(0, Long::sum);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("Time consuming:" + Duration.between(start, end).toMillis()); // 220
    }
}

Optional container class

The optional class (java.util.Optional) is a container class that represents the existence or nonexistence of a value. Originally, null was used to indicate the nonexistence of a value. Now optional can better express this concept. And you can avoid null pointer exceptions.

  • Optional. Of (T): create an optional instance
  • Optional.empty(): create an empty optional instance
  • Optional. Ofnullable (T): if t is not null, create an optional instance; otherwise, create an empty instance
  • isPresent(): judge whether the value is included
  • Orelse (T): if the calling object contains a value, return the value; otherwise, return t
  • orElseGet(Supplier s): if the calling object contains a value, return the value; otherwise, return the value obtained by S
  • map(Function f): if there is a value, process it and return the processed Optional. Otherwise, return Optional empty()
  • flatMap(Function mapper): similar to map, the return value must be Optional
 @Test
public void test() {
    // Optional. Of (T): encapsulates an optional instance, where the value t cannot be null, otherwise there is a NullPointException
    Optional<Employee> optional1 = Optional.of(new Employee());
    System.out.println(optional1.get());
    // Running result: Employee{name='null', age=null, salary=null, status=null}

    // Optional.empty(): encapsulates an empty optional object (encapsulates null)
    Optional<Employee> optional2 = Optional.empty();
    System.out.println(optional2.get());
    // Running result: exception in thread "main" Java util. NoSuchElementException: No value present
    //	        at java.util.Optional.get(Optional.java:135)
    //	        at TestOptional.main(TestOptional.java:16)

    // Optional. Ofnullable (T): non null is the same as the of method, and null is the same as empty()
    Optional<Employee> optional3 = Optional.ofNullable(new Employee());
    // isPresent(): judge whether it contains a value
    if (optional3.isPresent()) {
        System.out.println(optional3.get());
    }
    // Running result: Employee{name='null', age=null, salary=null, status=null}

    // Orelse (T): returns a value if it contains a value; Otherwise, the alternative value t is returned
    Optional<Employee> optional4 = Optional.ofNullable(null);
    Employee Employee1 = optional4.orElse(new Employee(18, "Zhang San", 24, 12450, Employee.Status.FREE));
    System.out.println(Employee1);
    // Running result: Employee{name = 'Zhang San', age=24, salary=15000.1, status=FREE}

    // orElseGet(Supplier s): returns a value if it contains a value; Otherwise, the value obtained by s is returned
    Employee Employee2 = optional4.orElseGet(() -> new Employee());
    System.out.println(Employee2);
    // Running result: Employee{name='null', age=null, salary=null, status=null}

    // map(Function f): if there is a value, process it and return the processed Optional. Otherwise, return Optional empty()
    Optional<Employee> optional5 = Optional.ofNullable(new Employee(15, "Zhang San", 24, 38462, Employee.Status.FREE));
    Optional<String> stringOptional1 = optional5.map(Employee::getName);
    System.out.println(stringOptional1.get());
    // Operation result: Zhang San

    // flatMap(Function mapper): similar to map, the return value must be Optional
    Optional<String> stringOptional2 = optional5.flatMap(x -> Optional.of(x.getName()));
    System.out.println(stringOptional2.get());
    // Operation result: Zhang San
}

Added by fleabel on Fri, 24 Dec 2021 01:06:54 +0200