本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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的 函数式编程,该包中的函数式接口有:
序号 | 接口 & 描述 |
---|---|
1 | BiConsumer代表了一个接受两个输入参数的操作,并且不返回任何结果 |
2 | BiFunction代表了一个接受两个输入参数的方法,并且返回一个结果 |
3 | BinaryOperator代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
4 | BiPredicate代表了一个两个参数的boolean值方法 |
5 | BooleanSupplier代表了boolean值结果的提供方 |
6 | Consumer代表了接受一个输入参数并且无返回的操作 |
7 | DoubleBinaryOperator代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
8 | DoubleConsumer代表一个接受double值参数的操作,并且不返回结果。 |
9 | DoubleFunction代表接受一个double值参数的方法,并且返回结果 |
10 | DoublePredicate代表一个拥有double值参数的boolean值方法 |
11 | DoubleSupplier代表一个double值结构的提供方 |
12 | DoubleToIntFunction接受一个double类型输入,返回一个int类型结果。 |
13 | DoubleToLongFunction接受一个double类型输入,返回一个long类型结果 |
14 | DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function接受一个输入参数,返回一个结果。 |
16 | IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。 |
17 | IntConsumer接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction接受一个int类型输入参数,返回一个结果 。 |
19 | IntPredicate:接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer接受一个long类型的输入参数,无返回值。 |
26 | LongFunction接受一个long类型输入参数,返回一个结果。 |
27 | LongPredicateR接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier无参数,返回一个结果long类型的值。 |
29 | LongToDoubleFunction接受一个long类型输入,返回一个double类型结果。 |
30 | LongToIntFunction接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer接受一个object类型和一个double类型的输入参数,无返回值。 |
33 | ObjIntConsumer接受一个object类型和一个int类型的输入参数,无返回值。 |
34 | ObjLongConsumer接受一个object类型和一个long类型的输入参数,无返回值。 |
35 | Predicate接受一个输入参数,返回一个布尔值结果。 |
36 | Supplier无参数,返回一个结果。 |
37 | ToDoubleBiFunction接受两个输入参数,返回一个double类型结果 |
38 | ToDoubleFunction接受一个输入参数,返回一个double类型结果 |
39 | ToIntBiFunction接受两个输入参数,返回一个int类型结果。 |
40 | ToIntFunction接受一个输入参数,返回一个int类型结果。 |
41 | ToLongBiFunction接受两个输入参数,返回一个long类型结果。 |
42 | ToLongFunction接受一个输入参数,返回一个long类型结果。 |
43 | UnaryOperator接受一个参数为类型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中间操作(流水线式中间操作)
多个中间操作可以连接起来形成一个流水线,除非流水线终止操作,否则中间操作不会执行任何处理。 终止操作时一次性全部处理,称为“延迟加载”。
筛选与切片
常用方法:
方法 | 描述 |
---|---|
filter | filter 方法用于通过设置的条件过滤出元素。 |
limit | limit 方法用于获取指定数量的流。 |
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)
常用方法:
方法 | 描述 |
---|---|
map | map - 接收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());