Easily ignored method identity() in Java Function

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 * Represents a function that accepts a parameter and produces a result.
 *       
 * @param <T> the type of the input to the function
 * Parameter < T > is the input type of the function
 * @param <R> the type of the result of the function
 * Parameter < R > is the return type of the function
 * @since 1.8
 */
// Indicate functional interface
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     * Apply this function to the given parameter.
     * @param t Function parameters
     * @return R Function return type
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     * Returns a composed function that first applies this function to
     * its input, and then applies the {@code after} function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     * Returns a composite function that first applies the function to its input and then to the result. If an exception is thrown on the evaluation of any function, it is inherited to the caller of the composite function.
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     * Returns a function that always returns its input argument.
     * This method returns a function that returns the input parameters.
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

Previously, we only knew that Function is a functional interface and supports Lambda expressions. Today, we only know the identity() of Function due to our needs.

The identity() of Function returns t - > t, which is itself.
In other words, identity() can be changed to T - > t

Today's demand is probably:

With list < user >, use stream() to get a key value pair map < ID, user >.

// structure Map Key value pair, key:Integer, value:IndexEntity
// key For the indicator entity id,value Is the corresponding indicator entity
Map<Integer, IndexEntity> map = indexEntities.stream().collect(Collectors.toMap(IndexEntity::getId, Function.identity()));

 

IndexEntity::getId is a new method reference from Java 8.

java8 feature function identity()

Function. What is identity()?

  1. //Convert Stream to container or Map
  2. Stream<String> stream = Stream.of("I", "love", "you", "too");
  3. Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));

If Function is an interface, then Function What does identity () mean? The explanation is as follows:

Java 8 allows specific methods to be added to the interface. There are two specific methods in the interface: default method and static method. identity() is a static method of Function interface.
Function.identity() returns a Lambda expression object whose output is the same as the input, which is equivalent to a Lambda expression in the form of T - > t.

The JDK source code of the identity() method is as follows:

  1. static Function identity() {
  2. return t -> t;
  3. }

Function. Application of identity()

In the following code, Task::getTitle needs a task and generates a key with only one title. Task - > task is a lambda expression used to return itself. In the above example, a task is returned.

  1. private static Map<String, Task> taskMap(List<Task> tasks) {
  2. return tasks.stream().collect(toMap(Task::getTitle, task -> task));
  3. }

You can use the default method identity in the Function interface to make the above code more concise and more direct in transmitting the developer's intention. The following is the code using the identity Function.

  1. import static java.util.function.Function.identity;
  2.  
  3. private static Map<String, Task> taskMap(List<Task> tasks) {
  4. return tasks.stream().collect(toMap(Task::getTitle, identity()));
  5. }

Function.identity() or t->t?

  1. Arrays.asList("a", "b", "c")
  2. .stream()
  3. .map(Function.identity()) // <- This,
  4. .map(str -> str) // <- is the same as this.
  5. .collect(Collectors.toMap(
  6. Function.identity(), // <-- And this,
  7. str -> str)); // <-- is the same as this.

In the above code, why use function Identity () instead of STR - > STR? What's the difference between them?

In the above code, STR - > STR and function Identity () is no different because they are t - > t. But sometimes we can't use function Identity, see the following example:

  1. List list = new ArrayList<>();
  2. list.add(1);
  3. list.add(2);

The following code can run successfully:

int[] arrayOK = list.stream().mapToInt(i -> i).toArray();

But if you write like this:

int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();

There will be an error when running, because the parameter required by mapToInt is ToIntFunction type, but ToIntFunction type has nothing to do with Function

Lambda expression, functional interface, method reference and Optional in new features of Java 8

1. Java8 Lambda expression

Lambda expressions, also known as closures, allow us to pass functions to a method as parameters, or treat the code itself as data.

Early Java developers could only use anonymous inner classes to implement Lambda expressions.

The simplest can be composed of comma separated parameter list, - > symbol and statement block.

For example:

// Example 1
// parameter e The type of is inferred by the compiler
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

// Example 2
// Of course, you can also write the type of execution parameter
Arrays.asList( "a", "b", "d" ).forEach((String e)-> System.out.println( e ) );

// Example 3
// When there are multiple parameters
Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));

// Example 4
// When Lambda When the statement block has only one line, it can not be used return sentence.
Arrays.asList( "a", "b", "d" ).sort((e1,e2)-> e1.compareTo(e2));

ps: remember to add parentheses to the parameter list when there are multiple parameters or the parameter type needs to be specified.

2. Functional interface

A functional interface is an interface that has only one abstract method, but can have multiple non abstract methods.

Function: such an interface can be implicitly converted into a Lambda expression.

As long as a developer adds a function to the interface, the interface will no longer be a functional interface, resulting in compilation failure. In order to solve this problem and explicitly explain that an interface is a functional interface, Java8 provides a special annotation * * @ functional interface * * all relevant interfaces in the Java library have this annotation.

@FunctionalInterface
interface Addtions {
    int test(int a, int b);// I'm the core
    default void hello() {
        System.out.println("I will not affect the definition of functional interfaces");
    }
    static void hello1(){
        System.out.println("Nor will I affect the definition of functional interfaces");
    }
}

Several common interfaces:

  • java.util.function.Function
    • R apply(T t);
  • java.util.function.Supplier
    • T get();
  • java.util.function.Predicate
    • boolean test(T t);
  • java.util.function.Consumer
    • void accept(T t);
  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.lang.reflect.InvocationHandler

Four black marked functional interfaces are often used when writing lamdba expressions, focusing on their method return values and method parameters.

3. Default and static methods of interface

Java 8 allows us to add a non abstract method implementation to the interface. We only need to use the keyword "default". This feature is also called extension method. An example is as follows:

  • The default method can be overridden by the implementation class Override
class FunctionalInterfaceTest implements Formula{
    @Override
    public double calculate(int a) {
        return 0;
    }
    // Can be rewritten sqrt method.
    @Override
    public double sqrt(int a) {
        return Formula.super.sqrt(a);
    }
}
@FunctionalInterface
interface Formula {
    double calculate(int a);
    
// This method(Default method)Can be overridden by implementation classes
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
    static void hello1(){
        System.out.println("I'm new here(JAVA8),My name is static method,");
    }
}

4. Method reference

Method reference enables developers to directly reference existing methods, Java class construction methods or instance objects. Method reference and Lambda expression are used together to make the construction method of Java class look compact and concise without many complex template codes.

It can be seen that the effect of using Lambda expression is the same as that of using method reference, but sometimes using method reference will simplify the code

  • Constructor reference
    • Class name:: new
  • Static method reference
    • Class name: static method
  • Object method reference
    • Class name: method
    • This method can be used when the first parameter in the parameter list of Lambda expression is the caller of the instance method and the second parameter (or no parameter) is the parameter of the instance method.
  • Instance method reference
    • Instance object:: member method
    • Get an instance object first
public class Test {
    private String name;
    public String getName() {
        return this.name;
    }
    public Test(String name) {
        this.name = name;
    }
    public static String staticMethod(){
        return "I'm a static method!";
    }
    
    public static void main(String[] args) {
        Test test1 = new Test("Xiao Ming");
        
        // Lambda expression
        Supplier<String> func1 = () -> test1.getName();
        System.out.println("Lambda Expression test:" + func1.get());
        
        // Instance method reference
        Supplier<String> func2 = test1::getName;
        System.out.println("Method reference test:" + func2.get());
        
        // Static method reference
        Supplier<String> func3 = Test::staticMethod;
        System.out.println("Static method reference test:" + func3.get());

        // Construction method reference(Constructor reference) 
        Function<String, Test> func4 = Test::new;
        Test test2 = func4.apply("xxx");
        System.out.println("Construction method reference test:" + test2);
        
        // Object method reference
        // Test Is the class name, getName Method for member.
        Function<Test, String> func5 = Test::getName;
        System.out.println("Object method test reference:" + func5.apply(test1));
    }
}

5,Optional

The most common bug in Java applications is NullPointerException,

For example, compare whether two strings are equal

s1.equals(s2). If s1==null, the console will become popular as soon as it runs.

So Java 8 provides options to solve this problem.

  • isPresent(): if the Optional instance holds a non null value, the method returns true; otherwise, it returns false
  • orElseGet(): if the Optional instance holds null, it can accept the default value generated by a lambda expression
  • map(): you can convert the value of an existing Opetional instance to a new value
  • orElse(): when the Opetional instance holds null, it returns the default value passed in. The method is similar to orElseGet().
  • filter(): if the optional instance is not null and the lambda expression in the filter returns true, an optional instance will be returned; Otherwise, an empty optional is returned.
    • If a value is present, and the value matches the given predicate,return an {@code Optional} describing the value, otherwise return an empty {@code Optional}.

  1. When the optional instance is null
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

// The following is the output result
Full Name is set? false
Full Name: [none]
Hey Stranger!

  2. When the optional instance is not null

Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );        
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); 
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ));

//Output results
First Name is set? true
First Name: Tom
Hey Tom!

Explore Java8: (I) the use of Stream

 
Reprint

The Java 8 API adds a new abstraction called Stream, which allows you to process data in a declarative way.

Stream uses an intuitive way similar to querying data from a database with SQL statements to provide a high-level abstraction for the operation and expression of Java collections.

Stream API can greatly improve the productivity of Java programmers and enable programmers to write efficient, clean and concise code.

This style regards the set of elements to be processed as a stream, which is transmitted in the pipeline and can be processed on the nodes of the pipeline, such as filtering, sorting, aggregation, etc.

The element flow is processed by intermediate operation in the pipeline, and finally the result of the previous processing is obtained by final operation.

What is Stream?

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

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

Unlike previous Collection operations, Stream operations have two basic features:

  • Pipelining: all intermediate operations will return the stream object itself. In this way, multiple operations can be connected in series into a pipe, just like the flow style. Doing so optimizes operations such as laziness and short circuiting.
  • Internal iteration: in the past, the collection was traversed through Iterator or for each to explicitly iterate outside the collection, which is called external iteration. Stream provides a way of internal iteration, which is realized through visitor mode.

1, Creation of flow

  • stream() − creates a serial stream for the collection.
  • parallelStream() − creates a parallel stream for the collection. Parallel stream is actually a stream executed in parallel It may improve the speed of your multithreaded tasks through the default ForkJoinPool. Parallel streams may be unordered during traversal.
public class ParallelStream {
	public static void main(String[] args) {
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
		numbers.stream().forEach(System.out::print);  
	}
}

2, forEach, map, filter, limit, sorted

numbers.stream().forEach(System.out::print);  
numbers.stream().forEach(i->System.out.print(i));  

The above two methods are equivalent.

forEach

forEach is used to iterate the data in the stream. For example, forEach is used in the above operation to create the stream. After looking at the above example, it won't be difficult to understand forEach. It should be noted that the forEach operation cannot change the traversal object itself.

Map

The map method is used to map each element to the corresponding result. In most cases, it is used to process data. The following is a code to increase the position elements of the original list by 2:

public class MapDemo {
	public static void main(String[] args) {
		List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
		List<Integer> outPutList = numbers.stream().map(i -> i + 2).distinct().collect(Collectors.toList());
		outPutList.forEach(n->System.out.print(n+" "));
	}
}

filter

The filter method is used to filter out elements through the set conditions. The following code snippet uses the filter method to filter out empty strings:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// Gets the number of empty strings
int count = strings.stream().filter(string -> string.isEmpty()).count();

Limit

The limit method is used to obtain a specified number of streams. The following code snippet prints out 10 pieces of data using the limit method:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

Another common method is to cooperate with the skip() method to perform paging operations.

int pageSize=10;
int currentPage=1;
return pageList.stream()
		.skip(pageSize * (currentPage-1))
		.limit(pageSize)
		.collect(Collectors.toList());

sorted

The sorted method is used to sort streams. The following code snippet uses the sorted method to sort the 10 random numbers output:

Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

Collectors

Collectors can be used to return lists or strings. Collectors are used in the example of map described above. An example of rookie tutorial is given below:

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("Filter list: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merge string: " + mergedString);

3, Statistics

As the name suggests, statistics is used for statistical data. It is generally used for basic types such as int, double and long.

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();

System.out.println("Maximum number of in the list : " + stats.getMax());
System.out.println("Minimum number in the list : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("average : " + stats.getAverage());

Stream is introduced here first. We feel strange to stream code because we have just come into contact with the style of declarative programming. The next article should introduce the usage of lambda expression and Optional. We will use declarative programming style more.

Explore Java8: (II) use of Function interface

 
Reprint

Java 8 has added a new feature Function, which must be a functional operation as the name suggests. We know that the biggest feature of Java 8 is the functional interface. All interfaces annotated with @ FunctionalInterface annotation are functional interfaces. Specifically, all interfaces annotated with this annotation can be used in lambda expressions.

There are many interfaces marked with @ FunctionalInterface, but in this article, we mainly talk about Function. After understanding Function, other operations are easy to understand.

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
    /**
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    /**
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
}

In order to easily read the source code, we need to know some knowledge of generics. If you are already familiar with generics, you can skip this paragraph.

The generic type is jdk1 5. Through generic programming, the code can be shared by many different types, which can improve the reusability of the code. Since the focus of this article is not on generics, we only focus on the meaning of generics used in the above Function source code.

1. Generic class

A generic class uses < T > to indicate that it is a generic class. The return values of its internal member variables and functions can be generic < T >, and the identification of the Function source code is < T, R >, that is, two generic parameters. It will not be repeated here. For specific generic classes, see the articles on the Internet.

2. Generic methods and wildcards

Add a < T > after the method modifier to indicate that the method is a generic method, such as the < V > of the compose method in the source code of Function. Wildcards are also easy to understand. It is also an example of compose. We can see that the parameter of compose is a Function type. The parameter of Functin specifies that the first parameter must be the parent class of V, and the second parameter must inherit T, that is, the subclass of T.

Source code analysis

1.apply

After talking about the above, you can start to study the source code.

First, we know that Function is a generic class, in which two generic parameters T and R are defined. In Function, T represents the input parameter and R represents the returned result. Perhaps you are curious, why is there no specific logic in the source code of Function different from other java source codes?

In fact, it is easy to understand that Function is a Function whose Function is similar to the definition of Function in mathematics, (x,y) is almost the same as that of < T, R >.

 

y=f(x)y=f(x)

 

Therefore, there is no specific operation in the Function. We need to specify the specific operation for it. Therefore, the specific result returned by apply depends on the incoming lambda expression.

 R apply(T t);

for instance:

public void test(){
    Function<Integer,Integer> test=i->i+1;
    test.apply(5);
}
/** print:6*/

We use a lambda expression to define a behavior that causes i to increase by 1. We use parameter 5 to execute apply, and finally return 6. This is different from our previous view of Java. Before functional programming, we define a group of operations. The first thing we think of is to define a method, and then specify the incoming parameters to return the results we need. The idea of functional programming is not to consider the specific behavior, but to consider the parameters first. We can set the specific methods later.

Another example:

public void test(){
    Function<Integer,Integer> test1=i->i+1;
    Function<Integer,Integer> test2=i->i*i;
    System.out.println(calculate(test1,5));
    System.out.println(calculate(test2,5));
}
public static Integer calculate(Function<Integer,Integer> test,Integer number){
    return test.apply(number);
}
/** print:6*/
/** print:25*/

By passing in different functions, we can realize different operations in the same method. In the actual development, this can greatly reduce a lot of repeated code. For example, I have the Function of adding users in the actual project, but the users are divided into VIP and ordinary users, and there are two different adding logic. Then we can write two different logics first. In addition, this also separates logic from data, and we can realize the reuse of logic.

Of course, the logic in actual development may be very complex. For example, two methods F1 and F2 need two logic AB, but F1 needs a - > b, and F2 method needs B - > a. In this way, we can also use the method just now. The source code is as follows:

public void test(){
    Function<Integer,Integer> A=i->i+1;
    Function<Integer,Integer> B=i->i*i;
    System.out.println("F1:"+B.apply(A.apply(5)));
    System.out.println("F2:"+A.apply(B.apply(5)));
}
/** F1:36 */
/** F2:26 */

It's also very simple, but it's not complicated enough. If we need four logical ABCD for F1 and F2, it will become very troublesome for us to write like this.

2.compose and then

Compose and then can solve our problems. First look at the source code of compose

  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

compose receives a Function parameter. When returning, first execute apply with the passed logic, and then use the apply of the current Function.

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

andThen is the opposite of compose. It executes the current logic first and then the incoming logic.

That may not be intuitive enough. I can put it another way

compose is equivalent to B.apply(A.apply(5)), and then is equivalent to A.apply(B.apply(5)).

public void test(){
    Function<Integer,Integer> A=i->i+1;
    Function<Integer,Integer> B=i->i*i;
    System.out.println("F1:"+B.apply(A.apply(5)));
    System.out.println("F1:"+B.compose(A).apply(5));
    System.out.println("F2:"+A.apply(B.apply(5)));
    System.out.println("F2:"+B.andThen(A).apply(5));
}
/** F1:36 */
/** F1:36 */
/** F2:26 */
/** F2:26 */

We can see that the return value of the above two methods is a Function, so we can use the operation of builder mode.

B.compose(A).compose(A).andThen(A).apply(5);

This operation is very simple. You can try it yourself.

Explore Java8: (III) use of predict interface

 
Reprint

In the previous article, we learned how to use the Function interface. In this article, we learn another practical Function interface predict.

The source code of predict is very similar to that of Function. We can compare the two to analyze. Go directly to the source code of predict:

public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

T he type of < boolean > is an assertion of the interface, that is, the result of < boolean >. Like Function, the specific implementation of Predicate is determined by the lambda expression passed in.

boolean test(T t);

Next, let's take a look at the three important methods and, or and negate of the default implementation of predict

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

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

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

These three methods correspond to the three connection Symbols & &, |, and!, The basic usage is very simple. Let's give an example:

int[] numbers= {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
		List<Integer> list=new ArrayList<>();
		for(int i:numbers) {
			list.add(i);
		}
		Predicate<Integer> p1=i->i>5;
		Predicate<Integer> p2=i->i<20;
		Predicate<Integer> p3=i->i%2==0;
		List test=list.stream().filter(p1.and(p2).and(p3)).collect(Collectors.toList());
		System.out.println(test.toString());
/** print:[6, 8, 10, 12, 14]*/

We define three assertions p1,p2,p3. Now there is a list from 1 to 15. We need to filter this list. The above filter is to filter out all lists that are greater than 5 but less than 20 and are even.

If suddenly our needs change, we now need to filter out odd numbers. Then I can't change predict directly, because this condition may also be used in other places in the actual project. At this time, I only need to change the condition of predict in the filter.

List test=list.stream().filter(p1.and(p2).and(p3.negate())).collect(Collectors.toList());
/** print:[7, 9, 11, 13, 15]*/

We can directly reverse the p3 condition. Isn't it simple?

The return type of isEqual method is also Predicate, so we can also use it as a functional interface. We can use it as a = = operator.

		List test=list.stream()
            .filter(p1.and(p2).and(p3.negate()).and(Predicate.isEqual(7)))
            .collect(Collectors.toList());
/** print:[7] */

Posted by doobster on Wed, 11 May 2022 22:40:16 +0300