Java 8 Lambda表达式

64 阅读23分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Lambda表达式

Java 8 函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。

Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。

函数式接口可以对现有的函数友好地支持 lambda。

JDK 1.8 之前已有的函数式接口:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口:

  • java.util.function

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

序号接口 & 描述
1BiConsumer代表了一个接受两个输入参数的操作,并且不返回任何结果
2BiFunction代表了一个接受两个输入参数的方法,并且返回一个结果
3BinaryOperator代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4BiPredicate代表了一个两个参数的boolean值方法
5BooleanSupplier代表了boolean值结果的提供方
6Consumer代表了接受一个输入参数并且无返回的操作
7DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。
9DoubleFunction代表接受一个double值参数的方法,并且返回结果
10DoublePredicate代表一个拥有double值参数的boolean值方法
11DoubleSupplier代表一个double值结构的提供方
12DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。
13DoubleToLongFunction接受一个double类型输入,返回一个long类型结果
14DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。
15Function接受一个输入参数,返回一个结果。
16IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。
17IntConsumer接受一个int类型的输入参数,无返回值 。
18IntFunction接受一个int类型输入参数,返回一个结果 。
19IntPredicate:接受一个int输入参数,返回一个布尔值的结果。
20IntSupplier无参数,返回一个int类型结果。
21IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。
22IntToLongFunction接受一个int类型输入,返回一个long类型结果。
23IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。
24LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。
25LongConsumer接受一个long类型的输入参数,无返回值。
26LongFunction接受一个long类型输入参数,返回一个结果。
27LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。
28LongSupplier无参数,返回一个结果long类型的值。
29LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。
30LongToIntFunction接受一个long类型输入,返回一个int类型结果。
31LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。
32ObjDoubleConsumer接受一个object类型和一个double类型的输入参数,无返回值。
33ObjIntConsumer接受一个object类型和一个int类型的输入参数,无返回值。
34ObjLongConsumer接受一个object类型和一个long类型的输入参数,无返回值。
35Predicate接受一个输入参数,返回一个布尔值结果。
36Supplier无参数,返回一个结果。
37ToDoubleBiFunction接受两个输入参数,返回一个double类型结果
38ToDoubleFunction接受一个输入参数,返回一个double类型结果
39ToIntBiFunction接受两个输入参数,返回一个int类型结果。
40ToIntFunction接受一个输入参数,返回一个int类型结果。
41ToLongBiFunction接受两个输入参数,返回一个long类型结果。
42ToLongFunction接受一个输入参数,返回一个long类型结果。
43UnaryOperator接受一个参数为类型T,返回值类型也为T。

@FunctionInterface注解

使用@FunctionInterface的接口,该接口内只能有一个抽象方法(默认方法不限),如果添加了多个抽象方法,编译器就会警告。

lambda表达式的基础语法

Java8中引入了一个新的操作符->,该操作符称之为箭头操作符或Lambda操作符。箭头操作符讲Lambda表达式拆分成两部分:

左侧:Lambda 表达式的参数列表

右侧:Lambda 表达式中所执行的功能,即lambda体

Lambda四种语法格式
  • 语法格式一:无参数,无返回值 () -> System.out.println("hello")
Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        runnable.run();
        //使用lambda表达式
        Runnable runnable1 = () -> System.out.println("Hello Lambda!");
        runnable1.run();
  • 语法格式二:无参数,有返回值。若实现方法体只有一行,return需省略。Supplier<String> supplier = () -> "supplier".toUpperCase();,还可以简写Supplier<String> supplier = "supplier"::toUpperCase;
Supplier<String> supplier = () -> {
    String str = "supplier";
   return str.toUpperCase();
};
System.out.println(supplier.get());
  • 语法格式三:有一个参数,并且无返回值(x) -> System.out.println(x);若参数只有一个参数,小括号可以不写;若lambda体中有多条语句,需要使用大括号{};
Consumer<Integer> consumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer num) {
                ++num;
                System.out.println(num);
            }
        };
        consumer.accept(10);
        //使用lambda表达式,(num),小括号可以省略
        IntConsumer consumer2 = (num) -> {
            ++num;
            System.out.println(num);
        };
        consumer2.accept(12);
  • 语法格式四:有多个参数,有返回值,若lambda体中只有一条语句,return可以省略;参数列表的参数类型可以省略,JVM会根据上下文自动推断,即类型推断。
Comparator<Integer> comparator = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1, o2);
        }
    };
    System.out.println(comparator.compare(1, 1));
​
    //使用lambda表达式
    Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);
    int compare = comparator1.compare(1, 2);
    System.out.println(compare);

Lambda四大核心接口

  • 消费型接口Consumer,有参数,没有返回值。可以对参数进行一些操作。

    public interface Consumer<T> {
        void accept(T t);
    }
    
  • 供给型接口Supplier,无参数,有返回值。

    public interface Supplier<T> {
        T get();
    }
    
  • 函数式接口Function<T, R>,有参数,有返回值。

    public interface Function<T, R> {
        R apply(T t);
    }
    
  • 断言型接口,有参数,返回值是布尔值。

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

方法引用

在Java 8之前只能进行值传递,方法是不能传递的。如果你想调用一个方法你必须先获取到它所在的类的实例,然后再通过实例去调用这个方法,但是Java 8新增了方法引用这个新特性可以让你直接把方法当做值来传递。

若Lambda体中的内容已经存在对应的方法实现,即存在一个方法,参数列表和返回值于函数是接口一致,方法的实现内容也和Lambda体中的内容相同。则可直接使用方法引用

如果我们想要调用的方法拥有一个名字,我们就可以通过它的名字直接调用它。

因为函数式接口的方法参数对应于隐式方法调用时的参数,所以被引用方法签名可以通过放宽类型,装箱以及组织到参数数组中的方式对其参数进行操作,就像在调用实际方法一样。

方法引用的种类(Kinds of method references)

方法引用有很多种,它们的语法如下:

  • 静态方法引用:ClassName::methodName
  • 实例上的实例方法引用:instanceReference::methodName
  • 超类上的实例方法引用:super::methodName
  • 类型上的实例方法引用:ClassName::methodName
  • 构造方法引用:Class::new。调用的构造方法是和函数式接口的参数类型一致的。
  • 数组构造方法引用:TypeName[]::new
// 静态方法引用:`ClassName::methodName`
Comparator<Integer> com1 = (x,y)->Integer.compare(x,y); //普通的lambda表达式
Comparator<Integer> com2 = Integer::compare; //lambda方法引用方式
int compare1 = com1.compare(1, 2);
int compare2 = com2.compare(1, 2);
System.out.println(compare1);
System.out.println(compare2);
​
// 实例上的实例方法引用:`instanceReference::methodName`
Employee employee = new Employee();
Supplier<Integer> sup1 = () -> employee.getAge();   //普通的lambda表达式
Supplier<Integer>  sup2  = employee::getAge;   //lambda方法引用方式
Integer age1 = sup1.get();
Integer age2 = sup2.get();
System.out.println(age1);
System.out.println(age2);
​
//类型上的实例方法引用:`ClassName::methodName`
BiPredicate<String, String> bp1 =(x,y)->x.equals(y);  //普通的lambda表达
BiPredicate<String, String> bp2 = String::equals;   //lambda方法引用方式
boolean test1 = bp1.test("哈哈", "哈哈");
boolean test2 = bp2.test("哈哈", "哈哈");
System.out.println(test1);
System.out.println(test2);
​
//构造方法引用:`Class::new`
Supplier<Employee> supplier1 = () -> new Employee();  //普通的lambda表达
Supplier<Employee> supplier2 = Employee::new; //lambda方法引用方式
Employee employee1 = supplier1.get();
Employee employee2 = supplier2.get();
System.out.println(employee1);
System.out.println(employee2);
​
// 数组构造方法引用:`TypeName[]::new`
Function<Integer,String[]> fun1 = (x) -> new String[x];
Function<Integer,String[]> fun2 = String[] :: new;
String[] str1 = fun1.apply(10);
String[] str2 = fun2.apply(20);
System.out.println(str1.length);
System.out.println(str2.length);

Stream-流

流Stream简单介绍

  • Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。它可以让你以一种声明的方式处理数据。
  • Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
  • 这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等;元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
  • 流还天然的支持并行操作,也就不用去写复杂的多线程的代码。

Stream(流)主要概念

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

Stream抽象接口

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> var1);
​
    <R> Stream<R> map(Function<? super T, ? extends R> var1);
​
    IntStream mapToInt(ToIntFunction<? super T> var1);
​
    LongStream mapToLong(ToLongFunction<? super T> var1);
​
    DoubleStream mapToDouble(ToDoubleFunction<? super T> var1);
​
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> var1);
​
    IntStream flatMapToInt(Function<? super T, ? extends IntStream> var1);
​
    LongStream flatMapToLong(Function<? super T, ? extends LongStream> var1);
​
    DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> var1);
​
    Stream<T> distinct();
​
    Stream<T> sorted();
​
    Stream<T> sorted(Comparator<? super T> var1);
​
    Stream<T> peek(Consumer<? super T> var1);
​
    Stream<T> limit(long var1);
​
    Stream<T> skip(long var1);
​
    void forEach(Consumer<? super T> var1);
​
    void forEachOrdered(Consumer<? super T> var1);
​
    Object[] toArray();
​
    <A> A[] toArray(IntFunction<A[]> var1);
​
    T reduce(T var1, BinaryOperator<T> var2);
​
    Optional<T> reduce(BinaryOperator<T> var1);
​
    <U> U reduce(U var1, BiFunction<U, ? super T, U> var2, BinaryOperator<U> var3);
​
    <R> R collect(Supplier<R> var1, BiConsumer<R, ? super T> var2, BiConsumer<R, R> var3);
​
    <R, A> R collect(Collector<? super T, A, R> var1);
​
    Optional<T> min(Comparator<? super T> var1);
​
    Optional<T> max(Comparator<? super T> var1);
​
    long count();
​
    boolean anyMatch(Predicate<? super T> var1);
​
    boolean allMatch(Predicate<? super T> var1);
​
    boolean noneMatch(Predicate<? super T> var1);
​
    Optional<T> findFirst();
​
    Optional<T> findAny();
​
    static <T> Stream.Builder<T> builder() {
        return new StreamBuilderImpl();
    }
​
    static <T> Stream<T> empty() {
        return StreamSupport.stream(Spliterators.emptySpliterator(), false);
    }
​
    static <T> Stream<T> of(T var0) {
        return StreamSupport.stream(new StreamBuilderImpl(var0), false);
    }
​
    @SafeVarargs
    static <T> Stream<T> of(T... var0) {
        return Arrays.stream(var0);
    }
​
    static <T> Stream<T> iterate(final T var0, final UnaryOperator<T> var1) {
        Objects.requireNonNull(var1);
        Iterator var2 = new Iterator<T>() {
            T t;
​
            {
                this.t = Streams.NONE;
            }
​
            public boolean hasNext() {
                return true;
            }
​
            public T next() {
                return this.t = this.t == Streams.NONE ? var0 : var1.apply(this.t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(var2, 1040), false);
    }
​
    static <T> Stream<T> generate(Supplier<T> var0) {
        Objects.requireNonNull(var0);
        return StreamSupport.stream(new OfRef(9223372036854775807L, var0), false);
    }
​
    static <T> Stream<T> concat(Stream<? extends T> var0, Stream<? extends T> var1) {
        Objects.requireNonNull(var0);
        Objects.requireNonNull(var1);
        java.util.stream.Streams.ConcatSpliterator.OfRef var2 = new java.util.stream.Streams.ConcatSpliterator.OfRef(var0.spliterator(), var1.spliterator());
        Stream var3 = StreamSupport.stream(var2, var0.isParallel() || var1.isParallel());
        return (Stream)var3.onClose(Streams.composedClose(var0, var1));
    }
​
    public interface Builder<T> extends Consumer<T> {
        void accept(T var1);
​
        default Stream.Builder<T> add(T var1) {
            this.accept(var1);
            return this;
        }
​
        Stream<T> build();
    }

Stream操作的三个步骤

一、创建Stream

从一个数据源,如集合、数组中获取流。

二、中间操作

一个操作的中间链,对数据源的数据进行操作。

三、终止操作

一个终止操作,执行中间操作链,并产生结果。

创建Stream的四种方式

1.数组:使用Arrays中的静态方法stream()获取数组流

// 1.通过Arrays中的静态方法stream()获取数组流
Employee[] emps = new Employee[10];
Stream<Employee> ArraysStream = Arrays.stream(emps);

2.集合:通过Collection得Stream()方法(串行流)或者 parallelStream()方法(并行流)创建Stream

// 2.通过Collection系列集合提供的stream()或parallelStream|()
List<String> list = new ArrayList<>();
Stream<String> stringStream = list.stream();
Stream<String> stringParallelStream = list.parallelStream();

3.通过Stream类中得 of()静态方法获取流

// 3.通过Stream类中的静态方法of()
Stream<String> stream = Stream.of("aa", "bb", "cc");

4.创建无限流:Stream.iterate(迭代)、Stream.generate(生成)

// 4.创建无限流
//  4.1迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
Stream<Integer> integerStream = Stream.iterate(2, (x) -> x * 2);
//  4.2生成(无限产生对象)
Stream<Double> doubleStream = Stream.generate(() -> Math.random());

Stream中间操作(流水线式中间操作)

多个中间操作可以连接起来形成一个流水线,除非流水线终止操作,否则中间操作不会执行任何处理。 终止操作时一次性全部处理,称为“延迟加载”。

筛选与切片

常用方法:

方法描述
filterfilter 方法用于通过设置的条件过滤出元素。
limitlimit 方法用于获取指定数量的流。
skip跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补。
distinct筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
//1.filter 方法用于通过设置的条件过滤出元素
Stream<Employee> stream = emps.stream().filter(employee -> {
    System.out.println("中间操作延迟加载");
    return employee.getAge() > 18;
});
// 终止操作:除非流水线终止操作,否则中间操作不会执行任何处理
// 内部迭代,将迭代交给JDK
stream.forEach(System.out::println);
// limit-截断流 方法用于获取指定数量的流。
// 迭代两次,终止迭代
emps.stream().filter(employee -> {
    System.out.println("短路");
    return employee.getAge() > 8;
}).limit(2).forEach(System.out::println);
//跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流,与limit(n)互补。
emps.stream().skip(2).forEach(System.out::println);
//筛选,去除重复元素。(引用类型通过流所生成元素的hashCode()和equals())
emps.stream().distinct().forEach(System.out::println);
映射(创建一个新的Stream)

常用方法:

方法描述
mapmap - 接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上
flatMap接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有的流连接成“一个流”。(类似集合的addAll()方法)
// 将雇员List的雇员名映射一个新元素(Stream)
Stream<String> nameStream = emps.stream().map(Employee::getName);
nameStream.forEach(System.out::println);
/**
  * 将字符串的每一个字符映射成一个流
  */
public static Stream<Character> filterCharacter(String str){
    List<Character> list = new ArrayList<>();
    for (Character ch : str.toCharArray()){
        list.add(ch);
    }
    return list.stream();
}
// 如果映射的是流对象,map映射的是这个流对象,而flatMap映射的是流对象里面的每一个元素。类似add和addAll的区别
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
Stream<Stream<Character>> streamStream = list.stream().map(TestStream::filterCharacter);
streamStream.forEach(System.out::println);
//java.util.stream.ReferencePipeline$Head@12bb4df8
//java.util.stream.ReferencePipeline$Head@4cc77c2e
//java.util.stream.ReferencePipeline$Head@7a7b0070
//java.util.stream.ReferencePipeline$Head@39a054a5

通过打印可知,如果想遍历流中的每一个字符,还需多一步遍历

List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
Stream<Stream<Character>> streamStream = list.stream().map(TestStream::filterCharacter);
streamStream.forEach(characterStream -> characterStream.forEach(System.out::println));
//a
//a
//...
//d

而flatMap方法的核心是先映射每个元素,然后进行操作,然后进行扁平化处理,最后汇集所有进行扁平化处理的结果集形成一个新的列表(扁平化:简而言之就是去除所有的修饰)。上面实例使用flatMap可以简化为:

List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
Stream<Character> characterStream = list.stream().flatMap(TestStream::filterCharacter);
characterStream.forEach(System.out::println);

对于数值型的流,可以使用mapToInt、mapToLong、mapToDouble、flatMapToInt、flatMapToLong、flatMapToDouble相关的映射方法,简化操作。

排序
方法描述
sorted()自然排序,调用元素类重写的Comparable接口的compareTo方法
sorted(Comparator<? super T> var1);定制排序,接收一个Comparator接口,自己实现排序规则
List<String> list = Arrays.asList("zzz","aaa", "bbb", "ddd", "ccc");
list.stream().sorted().forEach(System.out::print);
emps.stream().sorted((e1,e2)->{
    if (e1.getAge()==e2.getAge()){
        return e1.getName().compareTo(e2.getName());
    }else {
        return e1.getAge() - e2.getAge();
    }
}).forEach(System.out::println);

小结: Stream之所以“懒”的秘密也在于每次在使用Stream时,都会连接多个中间操作,并在最后附上一个结束操作。 像map()和filter()这样的方法是中间操作,在调用它们时,会立即返回另一个Stream对象。 而对于reduce()及findFirst()这样的方法,它们是终结操作,在调用它们时才会执行真正的操作来获取需要的值。如果有多个中间操作,Stream会将元素一个个的过滤,如果发现了符合条件的元素,会将该元素置入到下一个中间操作,不符合条件的元素,则不会进行下一步的中间操作。

Java 8 Streams API借助于短路操作优化了流处理。短路方法一旦满足条件就结束流处理。在通常的短路操作中,一旦满足条件,就会中断所有处于管道之前的中间操作。一些中间操作和终端操作具有此行为。

终止操作

查找与匹配
方法描述
min返回流中最小的元素
max返回流中最大的元素
count返回流中元素的总个数
anyMatch检查是否至少匹配一个元素
allMatch检查是否配置所有元素
noneMatch检查是否没有匹配所有元素
findFirst返回第一个元素
findAny返回当前流的任意元素。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。
// 查找年龄最大的
Optional<Employee> optionalEmployee = emps.stream().max(Comparator.comparingInt(Employee::getAge));
System.out.println(optionalEmployee.get());
// 统计雇员List中雇员的数量
System.out.println(emps.stream().count());
// 检查雇员里面是否存在年龄是8岁的
boolean anyMatch = emps.stream().anyMatch(employee -> employee.getAge() == 8);
System.out.println(anyMatch);
// 检查雇员是否是年龄都是8岁
boolean allMatch = emps.stream().allMatch(employee -> employee.getAge() == 8);
System.out.println(allMatch);
// 检查雇员中的年龄是否 没有68岁的
boolean noneMatch = emps.stream().noneMatch(employee -> employee.getAge() == 68);
System.out.println(noneMatch);
// 一般与排序协同使用
Optional<Employee> optionalEmployee = emps.stream().findFirst();
System.out.println(optionalEmployee.get());
Optional<Employee> anyOptionalEmployee = emps.parallelStream().findAny();
System.out.println(anyOptionalEmployee.get());
归约
方法描述
T reduce(T identity, BinaryOperator accumulator);可以将流中元素反复结合起来,得到一个值。返回T
Optional reduce(BinaryOperator accumulator);可以将流中元素反复结合起来,得到一个值。返回Optional

reduce英文含义:减少,缩小(尺寸、数量、价格等);简化; 归纳为。在Stream中可以理解成一个迭代运算器

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
Integer sum = integerList.stream().reduce(0, Integer::sum);
System.out.println(sum);
Optional<Double> optionalDouble = emps.stream().map(Employee::getSalary).reduce(Double::sum);
Double sumSalary = optionalDouble.get();
System.out.println(sumSalary);
收集
方法描述
collect将流转换为其他形式。接收一个Collector接口的实现,用于Stream中元素做汇总的方法。

Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。

Collectors是生产具体Collector的工具类。Collectors提供了很多静态方法,可以方便地创建常见收集器实例。

收集操作用的比较多,简单粘一下这两个类的代码看一下:

Collector

public interface Collector<T, A, R> {
    //supplier参数用于生成结果容器,容器类型为A
    Supplier<A> supplier();
    //accumulator用于消费元素,也就是归纳元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
    BiConsumer<A, T> accumulator();
    //combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
    BinaryOperator<A> combiner();
    // finisher用于将之前整合完的结果R转换成为A
    Function<A, R> finisher();
    // characteristics表示当前Collector的特征值,这是个不可变Set
    Set<Collector.Characteristics> characteristics();
    // 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果
    static <T, R> Collector<T, R, R> of(Supplier<R> var0, BiConsumer<R, T> var1, BinaryOperator<R> var2, Collector.Characteristics... var3) {
        Objects.requireNonNull(var0);
        Objects.requireNonNull(var1);
        Objects.requireNonNull(var2);
        Objects.requireNonNull(var3);
        Set var4 = var3.length == 0 ? Collectors.CH_ID : Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH, var3));
        return new CollectorImpl(var0, var1, var2, var4);
    }
    // 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为R
    static <T, A, R> Collector<T, A, R> of(Supplier<A> var0, BiConsumer<A, T> var1, BinaryOperator<A> var2, Function<A, R> var3, Collector.Characteristics... var4) {
        Objects.requireNonNull(var0);
        Objects.requireNonNull(var1);
        Objects.requireNonNull(var2);
        Objects.requireNonNull(var3);
        Objects.requireNonNull(var4);
        Set var5 = Collectors.CH_NOID;
        if (var4.length > 0) {
            EnumSet var6 = EnumSet.noneOf(Collector.Characteristics.class);
            Collections.addAll(var6, var4);
            var5 = Collections.unmodifiableSet(var6);
        }
​
        return new CollectorImpl(var0, var1, var2, var3, var5);
    }
​
    public static enum Characteristics {
        CONCURRENT,
        UNORDERED,
        IDENTITY_FINISH;
​
        private Characteristics() {
        }
    }
}

Collectors:Java文件注释太多,直接粘的class文件。方法较多,都很有用!

public final class Collectors {
    static final Set<Characteristics> CH_CONCURRENT_ID;
    static final Set<Characteristics> CH_CONCURRENT_NOID;
    static final Set<Characteristics> CH_ID;
    static final Set<Characteristics> CH_UNORDERED_ID;
    static final Set<Characteristics> CH_NOID;
​
    private Collectors() {
    }
​
    private static <T> BinaryOperator<T> throwingMerger() {
        return (var0, var1) -> {
            throw new IllegalStateException(String.format("Duplicate key %s", var0));
        };
    }
​
    private static <I, R> Function<I, R> castingIdentity() {
        return (var0) -> {
            return var0;
        };
    }
​
    public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> var0) {
        return new Collectors.CollectorImpl(var0, Collection::add, (var0x, var1) -> {
            var0x.addAll(var1);
            return var0x;
        }, CH_ID);
    }
​
    public static <T> Collector<T, ?, List<T>> toList() {
        return new Collectors.CollectorImpl(ArrayList::new, List::add, (var0, var1) -> {
            var0.addAll(var1);
            return var0;
        }, CH_ID);
    }
​
    public static <T> Collector<T, ?, Set<T>> toSet() {
        return new Collectors.CollectorImpl(HashSet::new, Set::add, (var0, var1) -> {
            var0.addAll(var1);
            return var0;
        }, CH_UNORDERED_ID);
    }
​
    public static Collector<CharSequence, ?, String> joining() {
        return new Collectors.CollectorImpl(StringBuilder::new, StringBuilder::append, (var0, var1) -> {
            var0.append(var1);
            return var0;
        }, StringBuilder::toString, CH_NOID);
    }
​
    public static Collector<CharSequence, ?, String> joining(CharSequence var0) {
        return joining(var0, "", "");
    }
​
    public static Collector<CharSequence, ?, String> joining(CharSequence var0, CharSequence var1, CharSequence var2) {
        return new Collectors.CollectorImpl(() -> {
            return new StringJoiner(var0, var1, var2);
        }, StringJoiner::add, StringJoiner::merge, StringJoiner::toString, CH_NOID);
    }
​
    private static <K, V, M extends Map<K, V>> BinaryOperator<M> mapMerger(BinaryOperator<V> var0) {
        return (var1, var2) -> {
            Iterator var3 = var2.entrySet().iterator();
​
            while(var3.hasNext()) {
                Entry var4 = (Entry)var3.next();
                var1.merge(var4.getKey(), var4.getValue(), var0);
            }
​
            return var1;
        };
    }
​
    public static <T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> var0, Collector<? super U, A, R> var1) {
        BiConsumer var2 = var1.accumulator();
        return new Collectors.CollectorImpl(var1.supplier(), (var2x, var3) -> {
            var2.accept(var2x, var0.apply(var3));
        }, var1.combiner(), var1.finisher(), var1.characteristics());
    }
​
    public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(Collector<T, A, R> var0, Function<R, RR> var1) {
        Set var2 = var0.characteristics();
        if (var2.contains(Characteristics.IDENTITY_FINISH)) {
            if (var2.size() == 1) {
                var2 = CH_NOID;
            } else {
                EnumSet var3 = EnumSet.copyOf(var2);
                var3.remove(Characteristics.IDENTITY_FINISH);
                var2 = Collections.unmodifiableSet(var3);
            }
        }
​
        return new Collectors.CollectorImpl(var0.supplier(), var0.accumulator(), var0.combiner(), var0.finisher().andThen(var1), var2);
    }
​
    public static <T> Collector<T, ?, Long> counting() {
        return reducing(0L, (var0) -> {
            return 1L;
        }, Long::sum);
    }
​
    public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> var0) {
        return reducing(BinaryOperator.minBy(var0));
    }
​
    public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> var0) {
        return reducing(BinaryOperator.maxBy(var0));
    }
​
    public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new int[1];
        }, (var1, var2) -> {
            var1[0] += var0.applyAsInt(var2);
        }, (var0x, var1) -> {
            var0x[0] += var1[0];
            return var0x;
        }, (var0x) -> {
            return var0x[0];
        }, CH_NOID);
    }
​
    public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new long[1];
        }, (var1, var2) -> {
            var1[0] += var0.applyAsLong(var2);
        }, (var0x, var1) -> {
            var0x[0] += var1[0];
            return var0x;
        }, (var0x) -> {
            return var0x[0];
        }, CH_NOID);
    }
​
    public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new double[3];
        }, (var1, var2) -> {
            sumWithCompensation(var1, var0.applyAsDouble(var2));
            var1[2] += var0.applyAsDouble(var2);
        }, (var0x, var1) -> {
            sumWithCompensation(var0x, var1[0]);
            var0x[2] += var1[2];
            return sumWithCompensation(var0x, var1[1]);
        }, (var0x) -> {
            return computeFinalSum(var0x);
        }, CH_NOID);
    }
​
    static double[] sumWithCompensation(double[] var0, double var1) {
        double var3 = var1 - var0[1];
        double var5 = var0[0];
        double var7 = var5 + var3;
        var0[1] = var7 - var5 - var3;
        var0[0] = var7;
        return var0;
    }
​
    static double computeFinalSum(double[] var0) {
        double var1 = var0[0] + var0[1];
        double var3 = var0[var0.length - 1];
        return Double.isNaN(var1) && Double.isInfinite(var3) ? var3 : var1;
    }
​
    public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new long[2];
        }, (var1, var2) -> {
            var1[0] += (long)var0.applyAsInt(var2);
            int var10002 = var1[1]++;
        }, (var0x, var1) -> {
            var0x[0] += var1[0];
            var0x[1] += var1[1];
            return var0x;
        }, (var0x) -> {
            return var0x[1] == 0L ? 0.0D : (double)var0x[0] / (double)var0x[1];
        }, CH_NOID);
    }
​
    public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new long[2];
        }, (var1, var2) -> {
            var1[0] += var0.applyAsLong(var2);
            int var10002 = var1[1]++;
        }, (var0x, var1) -> {
            var0x[0] += var1[0];
            var0x[1] += var1[1];
            return var0x;
        }, (var0x) -> {
            return var0x[1] == 0L ? 0.0D : (double)var0x[0] / (double)var0x[1];
        }, CH_NOID);
    }
​
    public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> var0) {
        return new Collectors.CollectorImpl(() -> {
            return new double[4];
        }, (var1, var2) -> {
            sumWithCompensation(var1, var0.applyAsDouble(var2));
            int var10002 = var1[2]++;
            var1[3] += var0.applyAsDouble(var2);
        }, (var0x, var1) -> {
            sumWithCompensation(var0x, var1[0]);
            sumWithCompensation(var0x, var1[1]);
            var0x[2] += var1[2];
            var0x[3] += var1[3];
            return var0x;
        }, (var0x) -> {
            return var0x[2] == 0.0D ? 0.0D : computeFinalSum(var0x) / var0x[2];
        }, CH_NOID);
    }
​
    public static <T> Collector<T, ?, T> reducing(T var0, BinaryOperator<T> var1) {
        return new Collectors.CollectorImpl(boxSupplier(var0), (var1x, var2) -> {
            var1x[0] = var1.apply(var1x[0], var2);
        }, (var1x, var2) -> {
            var1x[0] = var1.apply(var1x[0], var2[0]);
            return var1x;
        }, (var0x) -> {
            return var0x[0];
        }, CH_NOID);
    }
​
    private static <T> Supplier<T[]> boxSupplier(T var0) {
        return () -> {
            return (Object[])(new Object[]{var0});
        };
    }
​
    public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> var0) {
        return new Collectors.CollectorImpl(() -> {
            class OptionalBox implements Consumer<T> {
                T value = null;
                boolean present = false;
​
                OptionalBox() {
                }
​
                public void accept(T var1) {
                    if (this.present) {
                        this.value = var0.apply(this.value, var1);
                    } else {
                        this.value = var1;
                        this.present = true;
                    }
​
                }
            }
​
            return new OptionalBox();
        }, OptionalBox::accept, (var0x, var1) -> {
            if (var1.present) {
                var0x.accept(var1.value);
            }
​
            return var0x;
        }, (var0x) -> {
            return Optional.ofNullable(var0x.value);
        }, CH_NOID);
    }
​
    public static <T, U> Collector<T, ?, U> reducing(U var0, Function<? super T, ? extends U> var1, BinaryOperator<U> var2) {
        return new Collectors.CollectorImpl(boxSupplier(var0), (var2x, var3) -> {
            var2x[0] = var2.apply(var2x[0], var1.apply(var3));
        }, (var1x, var2x) -> {
            var1x[0] = var2.apply(var1x[0], var2x[0]);
            return var1x;
        }, (var0x) -> {
            return var0x[0];
        }, CH_NOID);
    }
​
    public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> var0) {
        return groupingBy(var0, toList());
    }
​
    public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> var0, Collector<? super T, A, D> var1) {
        return groupingBy(var0, HashMap::new, var1);
    }
​
    public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> var0, Supplier<M> var1, Collector<? super T, A, D> var2) {
        Supplier var3 = var2.supplier();
        BiConsumer var4 = var2.accumulator();
        BiConsumer var5 = (var3x, var4x) -> {
            Object var5 = Objects.requireNonNull(var0.apply(var4x), "element cannot be mapped to a null key");
            Object var6 = var3x.computeIfAbsent(var5, (var1) -> {
                return var3.get();
            });
            var4.accept(var6, var4x);
        };
        BinaryOperator var6 = mapMerger(var2.combiner());
        if (var2.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
            return new Collectors.CollectorImpl(var1, var5, var6, CH_ID);
        } else {
            Function var8 = var2.finisher();
            Function var9 = (var1x) -> {
                var1x.replaceAll((var1, var2) -> {
                    return var8.apply(var2);
                });
                return var1x;
            };
            return new Collectors.CollectorImpl(var1, var5, var6, var9, CH_NOID);
        }
    }
​
    public static <T, K> Collector<T, ?, ConcurrentMap<K, List<T>>> groupingByConcurrent(Function<? super T, ? extends K> var0) {
        return groupingByConcurrent(var0, ConcurrentHashMap::new, toList());
    }
​
    public static <T, K, A, D> Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> var0, Collector<? super T, A, D> var1) {
        return groupingByConcurrent(var0, ConcurrentHashMap::new, var1);
    }
​
    public static <T, K, A, D, M extends ConcurrentMap<K, D>> Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> var0, Supplier<M> var1, Collector<? super T, A, D> var2) {
        Supplier var3 = var2.supplier();
        BiConsumer var4 = var2.accumulator();
        BinaryOperator var5 = mapMerger(var2.combiner());
        BiConsumer var7;
        if (var2.characteristics().contains(Characteristics.CONCURRENT)) {
            var7 = (var3x, var4x) -> {
                Object var5 = Objects.requireNonNull(var0.apply(var4x), "element cannot be mapped to a null key");
                Object var6 = var3x.computeIfAbsent(var5, (var1) -> {
                    return var3.get();
                });
                var4.accept(var6, var4x);
            };
        } else {
            var7 = (var3x, var4x) -> {
                Object var5 = Objects.requireNonNull(var0.apply(var4x), "element cannot be mapped to a null key");
                Object var6 = var3x.computeIfAbsent(var5, (var1) -> {
                    return var3.get();
                });
                synchronized(var6) {
                    var4.accept(var6, var4x);
                }
            };
        }
​
        if (var2.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
            return new Collectors.CollectorImpl(var1, var7, var5, CH_CONCURRENT_ID);
        } else {
            Function var8 = var2.finisher();
            Function var9 = (var1x) -> {
                var1x.replaceAll((var1, var2) -> {
                    return var8.apply(var2);
                });
                return var1x;
            };
            return new Collectors.CollectorImpl(var1, var7, var5, var9, CH_CONCURRENT_NOID);
        }
    }
​
    public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> var0) {
        return partitioningBy(var0, toList());
    }
​
    public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> var0, Collector<? super T, A, D> var1) {
        BiConsumer var2 = var1.accumulator();
        BiConsumer var3 = (var2x, var3x) -> {
            var2.accept(var0.test(var3x) ? var2x.forTrue : var2x.forFalse, var3x);
        };
        BinaryOperator var4 = var1.combiner();
        BinaryOperator var5 = (var1x, var2x) -> {
            return new Collectors.Partition(var4.apply(var1x.forTrue, var2x.forTrue), var4.apply(var1x.forFalse, var2x.forFalse));
        };
        Supplier var6 = () -> {
            return new Collectors.Partition(var1.supplier().get(), var1.supplier().get());
        };
        if (var1.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
            return new Collectors.CollectorImpl(var6, var3, var5, CH_ID);
        } else {
            Function var7 = (var1x) -> {
                return new Collectors.Partition(var1.finisher().apply(var1x.forTrue), var1.finisher().apply(var1x.forFalse));
            };
            return new Collectors.CollectorImpl(var6, var3, var5, var7, CH_NOID);
        }
    }
​
    public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1) {
        return toMap(var0, var1, throwingMerger(), HashMap::new);
    }
​
    public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1, BinaryOperator<U> var2) {
        return toMap(var0, var1, var2, HashMap::new);
    }
​
    public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1, BinaryOperator<U> var2, Supplier<M> var3) {
        BiConsumer var4 = (var3x, var4x) -> {
            var3x.merge(var0.apply(var4x), var1.apply(var4x), var2);
        };
        return new Collectors.CollectorImpl(var3, var4, mapMerger(var2), CH_ID);
    }
​
    public static <T, K, U> Collector<T, ?, ConcurrentMap<K, U>> toConcurrentMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1) {
        return toConcurrentMap(var0, var1, throwingMerger(), ConcurrentHashMap::new);
    }
​
    public static <T, K, U> Collector<T, ?, ConcurrentMap<K, U>> toConcurrentMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1, BinaryOperator<U> var2) {
        return toConcurrentMap(var0, var1, var2, ConcurrentHashMap::new);
    }
​
    public static <T, K, U, M extends ConcurrentMap<K, U>> Collector<T, ?, M> toConcurrentMap(Function<? super T, ? extends K> var0, Function<? super T, ? extends U> var1, BinaryOperator<U> var2, Supplier<M> var3) {
        BiConsumer var4 = (var3x, var4x) -> {
            var3x.merge(var0.apply(var4x), var1.apply(var4x), var2);
        };
        return new Collectors.CollectorImpl(var3, var4, mapMerger(var2), CH_CONCURRENT_ID);
    }
​
    public static <T> Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> var0) {
        return new Collectors.CollectorImpl(IntSummaryStatistics::new, (var1, var2) -> {
            var1.accept(var0.applyAsInt(var2));
        }, (var0x, var1) -> {
            var0x.combine(var1);
            return var0x;
        }, CH_ID);
    }
​
    public static <T> Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> var0) {
        return new Collectors.CollectorImpl(LongSummaryStatistics::new, (var1, var2) -> {
            var1.accept(var0.applyAsLong(var2));
        }, (var0x, var1) -> {
            var0x.combine(var1);
            return var0x;
        }, CH_ID);
    }
​
    public static <T> Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> var0) {
        return new Collectors.CollectorImpl(DoubleSummaryStatistics::new, (var1, var2) -> {
            var1.accept(var0.applyAsDouble(var2));
        }, (var0x, var1) -> {
            var0x.combine(var1);
            return var0x;
        }, CH_ID);
    }
​
    static {
        CH_CONCURRENT_ID = Collections.unmodifiableSet(EnumSet.of(Characteristics.CONCURRENT, Characteristics.UNORDERED, Characteristics.IDENTITY_FINISH));
        CH_CONCURRENT_NOID = Collections.unmodifiableSet(EnumSet.of(Characteristics.CONCURRENT, Characteristics.UNORDERED));
        CH_ID = Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
        CH_UNORDERED_ID = Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED, Characteristics.IDENTITY_FINISH));
        CH_NOID = Collections.emptySet();
    }
​
    static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;
​
        CollectorImpl(Supplier<A> var1, BiConsumer<A, T> var2, BinaryOperator<A> var3, Function<A, R> var4, Set<Characteristics> var5) {
            this.supplier = var1;
            this.accumulator = var2;
            this.combiner = var3;
            this.finisher = var4;
            this.characteristics = var5;
        }
​
        CollectorImpl(Supplier<A> var1, BiConsumer<A, T> var2, BinaryOperator<A> var3, Set<Characteristics> var4) {
            this(var1, var2, var3, Collectors.castingIdentity(), var4);
        }
​
        public BiConsumer<A, T> accumulator() {
            return this.accumulator;
        }
​
        public Supplier<A> supplier() {
            return this.supplier;
        }
​
        public BinaryOperator<A> combiner() {
            return this.combiner;
        }
​
        public Function<A, R> finisher() {
            return this.finisher;
        }
​
        public Set<Characteristics> characteristics() {
            return this.characteristics;
        }
    }
​
    private static final class Partition<T> extends AbstractMap<Boolean, T> implements Map<Boolean, T> {
        final T forTrue;
        final T forFalse;
​
        Partition(T var1, T var2) {
            this.forTrue = var1;
            this.forFalse = var2;
        }
​
        public Set<Entry<Boolean, T>> entrySet() {
            return new AbstractSet<Entry<Boolean, T>>() {
                public Iterator<Entry<Boolean, T>> iterator() {
                    SimpleImmutableEntry var1 = new SimpleImmutableEntry(false, Partition.this.forFalse);
                    SimpleImmutableEntry var2 = new SimpleImmutableEntry(true, Partition.this.forTrue);
                    return Arrays.asList(var1, var2).iterator();
                }
​
                public int size() {
                    return 2;
                }
            };
        }
    }
}

实例:

List<String> nameStringList = emps.stream().map(Employee::getName).collect(Collectors.toList());
nameStringList.forEach(System.out::println);
Set<String> stringSet = emps.stream().map(Employee::getName).collect(Collectors.toSet());
stringSet.forEach(System.out::println);
HashSet<String> stringHashSet = emps.stream().map(Employee::getName)
    .collect(Collectors.toCollection(HashSet::new));
stringHashSet.forEach(System.out::println);
Long count = emps.stream().collect(Collectors.counting());
System.out.println(count);
//平均值
System.out.println(emps.stream().collect(Collectors.averagingDouble(Employee::getSalary)));
//求和
System.out.println(emps.stream().collect(Collectors.summingDouble(Employee::getSalary)));
//最大值
Optional<Employee> collect = emps.stream()
    .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(collect.get());
//分组
Map<Integer, List<Employee>> collect1 = emps.stream()
    .collect(Collectors.groupingBy(Employee::getAge));
System.out.println(collect1);
System.out.println("-------------------------");
//分区。薪资大于5000键为true,低于5000的键值为false
Map<Boolean, List<Employee>> booleanListMap = emps.stream()
    .collect(Collectors.partitioningBy(e -> e.getSalary() > 5000));
System.out.println(booleanListMap);
DoubleSummaryStatistics collect = emps.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(collect.getSum());
System.out.println(collect.getAverage());
System.out.println(collect.getMax());
String collect = emps.stream().map(Employee::getName).collect(Collectors.joining());
System.out.println(collect);
//连接串,前缀,后缀
String collect = emps.stream().map(Employee::getName).collect(Collectors.joining(",","begin","end"));
System.out.println(collect);
MapReduce

MapReduce是Google提出的大规模并行计算解决方案,应用于大规模廉价集群上的大数据并行处理。MapReduce以key/value的分布式存储系统为基础,通过元数据集中存储,数据以chunk为单位分布存储和数据chunk冗余复制来保证高可用性。

MapReduce是一种并行编程模型,将计算阶段分为两个阶段:Map阶段和Reduce阶段。首先把输入数据源分块,交给多个Map任务去执行,Map任务执行Map函数,根据某种规则对数据分类,写入本地硬盘。然后进入Reduce阶段,该阶段由Reduce函数把Map阶段具有相同key值的中间结果收集到相同Reduce结点进行合并处理,并将结果写入本地磁盘。最终结果通过合并Reduce任务的输出得到。

并行流

Fork/Join框架

Fork/Join框架可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。

Fork/Join框架与传统线程池的区别:

采用“工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能.

Stream并行流

Java8中将并行流进行了优化,可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。底层其实就是Fork/Join。

Instant start = Instant.now();
long reduce = LongStream.rangeClosed(0, 10000000000L).parallel().reduce(0, Long::sum);
Instant end = Instant.now();
System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());