Java8-Lambda & InterfaceAPI 入门

Scroll Down

1.Java8 Interface API入门

在看到Java8的新特性之后很想了解下Java8里面的新东西,所以根据Java8出现的新Stream类以及相关操作写一些自己简单的理解,如果哪里写的不对有大神看到还希望帮忙纠正,要是有写的不好的也请帮忙指正,我也希望自己在总结的过程中可以进步,也希望能帮到其他人。

使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出。
现在我们要定义一个Consumer对象,传统的方式是这样定义的:

 Consumer c = new Consumer() {
     @Override
     public void accept(Object o) {
         System.out.println(o);
     }
 };

Java8中的实现

 Consumer c = (o) -> {
     System.out.println(o);
 };

上面已说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当作该抽象方法的实现。

如果接口有多个抽象方法,编译器就不知道这段函数应该是实现哪个方法的了。

因此,=后面的函数体我们就可以看成是accept函数的实现。

输入:->前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的,如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()。

函数体:->后面的部分,即被{}包围的部分;可以是一段代码。

输出:函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值。

package com.troy.java.interfaceAPI;

/**
 * 函数式编程:
 * Java8之前
 * Consumer c = new Consumer() {
 *     @Override
 *     public void accept(Object o) {
 *         System.out.println(o);
 *     }
 * };
 * 在Java8中
 * Consumer c = (o) -> {
 *     System.out.println(o);
 * };
 * 输入:->前面的部分,即被()包围的部分。此处只有一个输入参数,实际上输入是可以有多个的,如两个参数时写法:(a, b);当然也可以没有输入,此时直接就可以是()。
 * 函数体:->后面的部分,即被{}包围的部分;可以是一段代码。
 * 输出:函数式编程可以没有返回值,也可以有返回值。如果有返回值时,需要代码段的最后一句通过return的方式返回对应的值。
 */
public class InterfaceAPITest {
    public static void main(String[] args){
        Consume m = apiWithoutReturn();
        m.doProcess("troy");
        ConsumeB cb = apiWithReturn();
        cb.doPorcessWithStr("java");

        System.out.println("=============输出面向接口编程的default实现,即面向接口编程写法=============");
        tryConsumeWithDefaulFun();
    }

    private static Consume apiWithoutReturn(){
        Consume m = (name)->{
            System.out.println(name);
        };
        return m;
    }

    private static ConsumeB apiWithReturn(){
        ConsumeB cm = (n) ->{
            System.out.println(n);
            return n;
        };
        return cm;
    }

    private static void tryConsumeWithDefaulFun(){
        Consume ma = t ->{
          System.out.println(t);
        };

        Consume mb = t ->{
            String t1 = t.concat("~~mb");
            System.out.println(t1);
        };

        //现调用ma的doPorcess然后在调用mb的doPorcess
        ma.andThen(mb).doProcess("hello");
//        ma.andThen(mb)
//          .doProcess("hello");


        //连续执行ma的doProcess()方法
        ma.andThen(ma)
          .andThen(ma)
          .andThen(ma)
          .doProcess("java8 API");
    }
}

/**
 * java8 面向接口编程只能有一个抽象方法,但是可以有多个default的实现方法,外部调用看起来类似就是面向方法的链式写法
 */
interface Consume{
    public void doProcess(String name);

    //可以定义default的andThen方法,然后在执行完当前Consume方法后在执行传入的Consume方法
    default Consume andThen(Consume m){
        return (t)->{doProcess(t);m.doProcess(t);};
    }

    default void last(Consume m){
        System.out.println("will output last before return");
        m.doProcess("default");
    }
}

interface ConsumeB{
    public String doPorcessWithStr(String name);

    //可以定义default的andThen方法,然后在执行完当前ConsumeB方法后在执行传入的ConsumeB方法
    default Consume andThen(ConsumeB m){
        return (t)->{doPorcessWithStr(t);m.doPorcessWithStr(t);};
    }

    default void last(ConsumeB m){
        System.out.println("will output last before return ~~B");
        m.doPorcessWithStr("default~~B");
    }
}

输出结果:

troy<br>
java<br>
=============输出面向接口编程的default实现,即面向接口编程写法=============<br>
hello<br>
hello~~mb<br>
java8 API<br>
java8 API<br>
java8 API<br>
java8 API<br>

2.Java 函数式接口

2.1-Consume

源码:

package java.util.function;

import java.util.Objects;

/**
 * Represents an operation that accepts a single input argument and returns no
 * result. Unlike most other functional interfaces, {@code Consumer} is expected
 * to operate via side-effects.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #accept(Object)}.
 *
 * @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

结合Consume接口源码可以看到Consume接口有一个无返回值但是有一个入参的apply(T t);我们可以根据业务自己实现apply方法,示例可以参考第一个例子,然后将实现Java自带的Consume就可以
2.2-Function

源码:

package java.util.function;
import java.util.Objects;
/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    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.
     *
     * @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.
     *
     * @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;
    }
}

通过源码可以看到,Function接口里面除了R apply(T t)方法,还有default实现的其他方法,这也就是说可以在Java8里面自己实现default的实现类,关于Function的default方法可以参考源码和示例

示例代码:

package com.troy.java.interfaceAPI;

import java.util.function.Function;

/**
 * java8 Function API
 * compose(function before)
 * andThen(function after)
 * identity()
 *
 * compose传进来的function会在当前function调用前执行,也就是说传进来的function返回值是当前function的入参
 * andThen传进来的function会在当前function调用后执行,也就是说当前function的返回值是传入的function的入参
 * identity不进行任何处理即输入与输出相等
 */
public class InterfaceAPITest1 {
    public static void main(String[] args){
        Function<String, Integer> f = o->{
            int len = ((String)o).length();
            System.out.println(o + " length is " + len);
            return len;
        };

        Function<Integer, Integer> f2 = o->{
            System.out.println("o is " +o);
            int result = 0;
            if(o instanceof Integer){
                result = Integer.valueOf(o)*2;
            }
            return result;
        };


        /**
         * 因为f2的入参来自远f方法,那么.apply("troy")中的troy是f方法的入参
         */
        int res = f2.compose(f).apply("troy");
        System.out.println(res);
    }
}

输出结果:

troy length is 4<br>
o is 4<br>
8<br>

2.3-Predicate

源码:

package java.util.function;

import java.util.Objects;

/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    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.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    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.
     *
     * @return 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.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    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)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return 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);
    }
}

我们可以根据Predicate声明一个断言或者判断逻辑,然后去结合业务去调用

示例代码:

package com.troy.java.interfaceAPI;

import java.util.function.Predicate;

public class InterfaceAPITest2 {
    public static void main(String[] args){
        Predicate<String> p = o -> {
            return o.equals("troy");
        };

        Predicate<String> p1 = o ->{
            return o.length() > 5;
        };
        System.out.println(p.test("abby"));
        System.out.println(p.negate().test("abby"));
        System.out.println(p.and(p1).test("troy"));
        System.out.println(p.or(p1).test("troy"));
    }
}

输出结果:

false<br>
true<br>
false<br>
true<br>

2.4-BiFunction

源码:

package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts two arguments and produces a result.
 * This is the two-arity specialization of {@link Function}.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object, Object)}.
 *
 * @param <T> the type of the first argument to the function
 * @param <U> the type of the second argument to the function
 * @param <R> the type of the result of the function
 *
 * @see Function
 * @since 1.8
 */
@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);

    /**
     * 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.
     *
     * @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
     */
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

类似于Function但是有一些不同,可以参考Function接口来学习,BiFunction学习需要注意的地方是参数类型和返回值类型,注意到这些类型那么BiFunction就很容易理解了,注意的是BiFunction中的三个参数可以相同也可以不同,不同的时候BiFunction就可以做很多事情

2.5-BinaryOperator
源码:

可以看到BinaryOperator是继承了BiFunction,通过观察参数和返回值类型可以看到BinaryOperator是三个参数都一样的BiFunction,这样来看就很容易理解BinaryFunction

package java.util.function;

import java.util.Objects;
import java.util.Comparator;

/**
 * Represents an operation upon two operands of the same type, producing a result
 * of the same type as the operands.  This is a specialization of
 * {@link BiFunction} for the case where the operands and the result are all of
 * the same type.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object, Object)}.
 *
 * @param <T> the type of the operands and result of the operator
 *
 * @see BiFunction
 * @see UnaryOperator
 * @since 1.8
 */
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    /**
     * Returns a {@link BinaryOperator} which returns the lesser of two elements
     * according to the specified {@code Comparator}.
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the lesser of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /**
     * Returns a {@link BinaryOperator} which returns the greater of two elements
     * according to the specified {@code Comparator}.
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the greater of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

示例代码:

有一点要提醒,在使用parallel处理并发的时候,一定要注意线程安全.示例代码reduceTest2_2()中,使用的List一定要是线程安全的List,如果我们使用的是List list = new ArrayList()那么当我们在parallel处理的时候会碰到最后的输出中结果会随机丢失值,这个是在使用parallel处理流的时候要特别注意的事情,有兴趣的同学可以尝试用普通的ArrayList,然后多执行几次就会碰到丢失记录的问题。

package com.troy.java.interfaceAPI;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * BiFunction需要三个参数,前两个是入参,第三个参数是返回值类型,三个参数类型可以一样;
 * BinaryOperator继承BiFunction,可以看作是三个参数一样的BiFunction
 * BinaryOperator的MaxBy和MinBy方法的参数来自于apply()方法,
 */
public class InterfaceAPITest3 {
    public static void main(String[] args){
        reduceTest();
        reduceTest1();
        reduceTest2_1();
        reduceTest2_2();
        recoginizeBiFunction();
    }

    /**
     * t1和t2的结果一样,其实t1就是t2的lambda写法,我们可以看到reduce操作最后会返回一个单一值
     * 因为在reduce操作的时候,会将stream里面每一次reduce计算的结果所谓下一次reduce操作的第一个输入参数
     * 第二个输入参数是这一次reduce操作从stream里面拿到的新值,但是要注意方法的返回值是一个Optional类型
     */
    private static void reduceTest(){
        System.out.println("==================================");
        List<String> list = new ArrayList();
        list.add("java hello");list.add("Python world");list.add("scala IT");
        String t = list.stream().reduce((r,x)->{
            String[] arr = r.split(" ");
            String[] arrX = x.split(" ");
            String r1 = arr[0].concat("~~~").concat(arrX[0]);
            String x1 = arr[1].concat("~~~").concat(arrX[1]);
            return r1.concat(" ").concat(x1);
        }).get();
        System.out.println(t);

        String t1 = list.stream().reduce(new BinaryOperator<String>() {
            @Override
            public String apply(String s, String s2) {
                String[] arr = s.split(" ");
                String[] arrX = s2.split(" ");
                String r1 = arr[0].concat("~~~").concat(arrX[0]);
                String x1 = arr[1].concat("~~~").concat(arrX[1]);
                return r1.concat(" ").concat(x1);
            }
        }).get();
        System.out.println(t1);
        System.out.println("==========end reduceTest==========");
    }

    /**
     * 这个方法和reduceTest的区别在于,reduce操作有了第一个初始值,即会将indentity做为第一次reduce操作的第一个参数开始计算
     * 同时方法的返回值类型等于indentity的类型,因此我们在最后不需要在去get可以直接拿到string类型的返回值
     */
    private static void reduceTest1(){
        System.out.println("==================================");
        List<String> list = new ArrayList();
        list.add("java hello");list.add("Python world");list.add("scala IT");
        String indentity = "language info";
        String t = list.stream().reduce(indentity,(r,x)->{
            String[] arr = r.split(" ");
            String[] arrX = x.split(" ");
            String r1 = arr[0].concat("~~~").concat(arrX[0]);
            String x1 = arr[1].concat("~~~").concat(arrX[1]);
            return r1.concat(" ").concat(x1);
        });
        System.out.println(t);

        String t1 = list.stream().reduce(indentity,new BinaryOperator<String>() {
            @Override
            public String apply(String s, String s2) {
                String[] arr = s.split(" ");
                String[] arrX = s2.split(" ");
                String r1 = arr[0].concat("~~~").concat(arrX[0]);
                String x1 = arr[1].concat("~~~").concat(arrX[1]);
                return r1.concat(" ").concat(x1);
            }
        });
        System.out.println(t1);
        System.out.println("=========end reduceTest1==========");
    }

    /**
     * 我们可以看到并行时的计算结果是18,而非并行时的计算结果是10!
     * 先看非并行计算:因为在非并行计算的时候第三个参数的函数体是不执行的,所以我们可以看作reduce方法是执行第一个参数和第二个参数也就是,第一步计算4 + 1 = 5,第二步是5 + 2 = 7,第三步是7 + 3 = 10;
     * 接下来分析并行模式下:在并行下先执行第一个参数和第二个参数的函数,最后在第三个函数体做一个整合。在并行处理时,每一个线程之间都是没有影响的,因此每个线程在调用第二个参数方法都是直接用第一个
     * 参数值(identity=4)做为原始值(由于Stream计算的延迟性,在调用最终方法前,都不会进行实际的运算,因此每个线程取到的identity值都是原始的4),
     * 因此计算过程现在是这样的:线程1:1 + 4 = 5;线程2:2 + 4 = 6;线程3:3 + 4 = 7;Combiner函数: 5 + 6 + 7 = 18
     */
    public static void reduceTest2_1(){
        System.out.println("==================================");
        int result1 = Stream.of(1, 2, 3).reduce(4, new BiFunction<Integer, Integer, Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                }
                , new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        System.out.println("does this function processed_result1");
                        return integer + integer2;
                    }
            });
        int result2 = Stream.of(1, 2, 3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                }
                , new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        System.out.println("does this function processed_result2");
                        return integer + integer2;
                    }
                });
        System.out.println("result1===result2 : "+ result1+"==="+result2 );

        /**
         * 通过result3可以验证在parallel的时候,先处理第一个和第二杆参数的函数,最后combain第三个函数,返回值是210进而也验证了我们的的理论也就是说先执行第一个和第二个参数,然后在根据第三个函数整合
         */
        int result3 = Stream.of(1, 2, 3).parallel().reduce(4, new BiFunction<Integer, Integer, Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        return integer + integer2;
                    }
                }
                , new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        System.out.println("does this function processed_result2");
                        return integer * integer2;
                    }
                });
        System.out.println("result3===: "+ result3 );

        System.out.println("========end reduceTest2_1=========");
    }

    /**
     * 第一个参数是reduce的初始值
     * 第二个参数是在并行阶段并行处理的方法,在三个参数的reduce方法声明中,BiFunction的第一个参数和第三个参数一样等于reduce第一个参数的类型,BiFunction的第三那个参数是BiFunction的返回类型
     * 第三个参数是前两个参数并行处理结束后来汇总处理结果的匿名函数BinaryOperator,如果当前调用reduce方法的stream不是并行处理的流,那么第三个参数可以忽略;
     * 注意事项,在reduce的源码里面所有accumulator.apply(u,t)中的第二个参数t都必须是stream里面的element,u根据情况有可能是identity的值,当有identity时,如果没有identity那么u是
     * stream的第一个element,accumulator随着reduce方法的参数确定具体哪一个函数是accumulator,所以在当前示例中BiFunction的第二个参数必须是String类型的数。
     */
    private static void reduceTest2_2(){
        System.out.println("==================================");
        Stream<String> s1 = Stream.of("aa", "ab", "c", "ad");
        Predicate<String> predicate = t -> t.contains("a");
        List<String> list = Collections.synchronizedList(new ArrayList<String>());
        s1.parallel().reduce(list, new BiFunction<List<String>, String, List<String>>() {
                    @Override
                    public List<String> apply(List<String> strings, String s) {
                        if(predicate.test(s)) {
                            strings.add(s);
                        }
                        return strings;
                    }
                },
                new BinaryOperator<List<String>>() {
                    @Override
                    public List<String> apply(List<String> strings, List<String> strings2) {

                        /**
                         *在这里可以看到strings和strings2是相等的,因为strings和string2来自于BiFunction并行计算出来的结果集,并且在BiFunction中的第一个参数来自于初始值identity
                         * 也就是说在不同线程的BiFunction中都用我们在第一个参数new出来的新ArrayList然后基于new的ArrayList处理了一样的BiFunction业务,最后在parallel的计算中返回每一次
                         * 线程计算的结果,因此可以理解为每一个线程都是用我们在reduce第一个参数new出来的Arraylist然后各自独立的处理了同样的逻辑然后返回该ArrayList,
                         * 因此最终返回的Arraylist也是一样的
                         */
                        System.out.println(strings == strings2);
                        System.out.println(strings.size());
                        return strings;
                    }
                }).stream().forEach(f->System.out.println(f));

        Stream<String> s2 = Stream.of("aa", "ab", "c", "ad");
        Predicate<String> predicate2 = t -> t.contains("a");
        List<String> list1 = Collections.synchronizedList(new ArrayList<String>());
        s2.parallel().reduce(list1, (r, t) -> {if (predicate2.test(t)) r.add(t);  return r; },
                (r1, r2) -> {System.out.println(r1.size());return r1; }).stream().forEach(f->System.out.println(f));
        System.out.println("=========end reduceTest2==========");
    }

    private static void recoginizeBiFunction(){
        System.out.println("==================================");
        BiFunction<Integer,Integer,Integer> biF = (f,u)->{
            return f+u;
        };

        BinaryOperator<Integer> bo = (f,x)->{
            return f-x;
        };
        /**
         * 返回值根据Comparator里面的实现方法判断,o1-o2>=0?0:-1;表示返回大的数
         */
        int i = BinaryOperator.maxBy(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2>=0 ? 0:-1;
            }
        }).apply(15,9);
        System.out.println(i);

        int j = BinaryOperator.maxBy(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2>=0 ? 0:-1;
            }
        }).apply(9,15);
        System.out.println(j);
        System.out.println("=====end recoginizeBiFunction=====");
    }
}

输出结果(输出结果展示的时候有些‘===’看不到了请忽略,因为markdown当作表黄给转换了):

==================================<br>
java~~~Python~~~scala hello~~~world~~~IT<br>
java~~~Python~~~scala hello~~~world~~~IT<br>
==========end reduceTest==========<br>
==================================<br>
language~~~java~~~Python~~~scala info~~~hello~~~world~~~IT<br>
language~~~java~~~Python~~~scala info~~~hello~~~world~~~IT<br>
=========end reduceTest1==========<br>
==================================<br>
does this function processed_result2<br>
does this function processed_result2<br>
result1===result2 : 10===18<br>
does this function processed_result2<br>
does this function processed_result2<br>
result3===: 210<br>
========end reduceTest2_1=========<br>
==================================<br>
true
3
true
3
true
3
aa
ad
ab
3
3
3
ab
ad
aa
=========end reduceTest2==========
==================================
15
15
=====end recoginizeBiFunction=====

参考博客地址:

https://blog.csdn.net/icarusliu/article/details/79495534

https://blog.csdn.net/icarusliu/article/details/79504602