有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家
public interface Stream<T> extends BaseStream<T, Stream<T>>
public interface IntStream extends BaseStream<Integer, IntStream>
public interface LongStream extends BaseStream<Long, LongStream>
public interface DoubleStream extends BaseStream<Double, DoubleStream>
上面这些原始类型流的工作方式与常规对象流基本是一样的,但还是略微存在一些区别:
IntFunction
代替Function
,IntPredicate
代替Predicate
。sum()
以及average()
,如下所示:Arrays.stream(new int[] {1, 2, 3}).average().ifPresent(System.out::println);
但是,偶尔我们也有这种需求,需要将常规对象流转换为原始类型流,这个时候,中间操作 mapToInt()
,mapToLong()
以及mapToDouble
就派上用场了
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
persons.stream().mapToInt(p->p.age).max().ifPresent(System.out::println);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
23
如果说,您需要将原始类型流装换成对象流,您可以使用 mapToObj()
来达到目的:
IntStream.range(1,4).mapToObj(String::valueOf).forEach(System.out::println);
中间操作返回Stream,终端操作返回void或者非stream
没有终端操作,中间操作是不会生效的.
/**
* 什么都不会输出,why
* 因为只有存在终端操作时中间操作才会被执行
*/
Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> {
System.out.println("filter: " + s);
return true;
});
/**
* sorted 水平调用(需要保存中间状态) 在水平调用前的所有垂直调用将变为水平调用
* filter,map,垂直执行(提高性能)
* 有状态的操作 distinct skip limit sorted reduce
*/
Stream.of("d2", "a2", "b1", "b3", "c")
.map(s -> {
System.out.println("map1: " + s);
return s.toUpperCase(); // 转大写
})
.sorted((s1, s2) -> {
System.out.printf("sort: %s; %s\n", s1, s2);
return s1.compareTo(s2); // 排序
})
.map(s -> {
System.out.println("map2: " + s);
return s.toLowerCase();
})
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("a"); // 过滤出以 a 为前缀的元素
})
.forEach(s -> System.out.println("forEach: " + s)); // for 循环输出
Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> {
System.out.println("filter: " + s);
return true;
});
执行此代码段时,您可能会认为,将依次打印 “d2”, “a2”, “b1”, “b3”, “c” 元素。然而当你实际去执行的时候,它不会打印任何内容。
为什么呢?
原因是:当且仅当存在终端操作时,中间操作操作才会被执行。
Java8 Stream 流是不能被复用的,一旦你调用任何终端操作,流就会关闭:
Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c");
// 终端操作会关闭流
boolean b = stream.anyMatch(item -> true);
Optional<String> any = stream.findAny();
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:469)
at StreamDemo.extracted7(StreamDemo.java:95)
at StreamDemo.main(StreamDemo.java:20)
为了克服这个限制,我们必须为我们想要执行的每个终端操作创建一个新的流链,例如,我们可以通过 Supplier
来包装一下流,通过 get()
方法来构建一个新的 Stream
流,如下所示:
Supplier<Stream<String>> supplier = () -> Stream.of("d2", "a2", "b1", "b3", "c");
// 终端操作会关闭流 流的复用
boolean b = supplier.get().anyMatch(item -> true);
Optional<String> any = supplier.get().findAny();
通过构造一个新的流,来避开流不能被复用的限制, 这也是取巧的一种方式。
List<String> list = getList();
int pageSizse = 10;
int total = list.size() / pageSizse;
if (list.size() % pageSizse != 0) {
total += 1;
}
for (int i = 0; i < total; i++) {
System.out.println(String.format("第%d页", i + 1));
list.stream().skip(i * pageSizse).limit(pageSizse).forEach(item -> System.out.println("item = " + item));
}
public static void main(String[] args) {
List<String> xxx = new ArrayList<>();
xxx.add("zss");
List<String> yyy = xxx.stream().filter(item -> item.equals("yyy")).collect(Collectors.toList());
System.out.println(yyy.size());
}
filter没有元素返回空集合,不会返回null
peek 窥视 偷看
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// peek 改变元素的内部状态(如果没有终端操作,这里是不会触发的) 可以这么用,但不建议
persons.stream().peek(p->p.age = p.age*2).forEach(System.out::println);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=11}
{name='Max', age=36}
{name='Peter', age=46}
{name='Pamela', age=46}
{name='David', age=22
peek() vs forEach()
forEach()
是一个最终操作。除此之外,peek()
和 forEach()
再无其他不同。
peek() 的典型用法:协助调试
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
persons.stream().filter(p -> p.age >= 18).peek(System.out::println).forEach(p->{
p.age=p.age*2;
System.out.println("p = " + p);
});
小结
forEach()
;peek()
;@Data
class Foo {
private String name;
private List<Bar> bars = new ArrayList<>();
public Foo(String name) {
this.name = name;
}
}
@Data
class Bar {
private String name;
public Bar(String name) {
this.name = name;
}
}
List<Foo> foos = new ArrayList<>();
for (int i = 1; i < 4; i++) {
Foo foo = new Foo("Foo" + i);
List<Bar> bars = new ArrayList<>();
for (int j = 1; j < 4; j++) {
bars.add(new Bar("Bar"+j+"<-"+foo.getName()));
}
foo.setBars(bars);
foos.add(foo);
}
for (Foo foo : foos) {
System.out.println("foo = " + foo);
}
foos.stream().flatMap(foo -> foo.getBars().stream()).forEach(bar-> System.out.println(bar.getName()));
foo = Foo(name=Foo1, bars=[Bar(name=Bar1<-Foo1), Bar(name=Bar2<-Foo1), Bar(name=Bar3<-Foo1)])
foo = Foo(name=Foo2, bars=[Bar(name=Bar1<-Foo2), Bar(name=Bar2<-Foo2), Bar(name=Bar3<-Foo2)])
foo = Foo(name=Foo3, bars=[Bar(name=Bar1<-Foo3), Bar(name=Bar2<-Foo3), Bar(name=Bar3<-Foo3)])
Bar1<-Foo1
Bar2<-Foo1
Bar3<-Foo1
Bar1<-Foo2
Bar2<-Foo2
Bar3<-Foo2
Bar1<-Foo3
Bar2<-Foo3
Bar3<-Foo3
Outer outer = new Outer();
Nested nested = new Nested();
outer.nested = nested;
Inner inner = new Inner();
nested.inner = inner;
inner.foo = "foo...";
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
取代
Outer outer = new Outer();
Nested nested = new Nested();
outer.nested = nested;
Inner inner = new Inner();
nested.inner = inner;
inner.foo = "foo...";
Optional.of(outer)
.flatMap(o -> Optional.ofNullable(o.nested))
.flatMap(n -> Optional.ofNullable(n.inner))
.flatMap(i -> Optional.ofNullable(i.foo))
.ifPresent(System.out::println);
List<EmergencyIot> compCode1 = detectionIots.stream().flatMap(item -> Optional.ofNullable(item.getEmergencyIots()).orElse(Collections.emptyList()).stream()).peek(item -> item.setCompCode("compCode")).collect(Collectors.toList());
List<EmergencyIot> compCode1 = detectionIots.stream().flatMap(item -> Optional.ofNullable(item.getEmergencyIots()).orElse(Collections.emptyList()).stream()).peek(item -> item.setCompCode("compCode")).collect(Collectors.toList());
List<String> strs = Arrays.asList("a", "b", "c");
strs.stream().map(String::toUpperCase).forEachOrdered(System.out::print);
System.out.println();
strs.stream().map(String::toUpperCase).forEach(System.out::print);
System.out.println();
strs.parallelStream().forEachOrdered(System.out::print);
System.out.println();
strs.parallelStream().forEach(System.out::print);
ABC
ABC
abc
bac
猜测:forEach与forEachOrdered在串行流下没区别
并行流parallelStream下forEachOrdered严格按照顺序(效率低),forEach与线程执行速度有关(效率高)
findAny(找到并返回任何一个(都行))、findFirst 、max与min返回值都是Optional
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// 在串行时findAny返回的是第一个元素与findFirst效果一样,在并行时返回的是处理最快的线程的结果,效率上比findFirst快
persons.stream().findAny().ifPresent(System.out::println);
persons.stream().findFirst().ifPresent(System.out::println);
persons.stream().max(Comparator.comparingInt(p -> p.age)).ifPresent(System.out::println);
persons.stream().min(Comparator.comparingInt(p -> p.age)).ifPresent(System.out::println);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
{name='Max', age=18}
{name='Max', age=18}
{name='Peter', age=23}
{name='David', age=12}
anyMatch、allMatch与noneMatch的返回值都是boolean
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// anyMatch 只要匹配到任意一个就停止并返回true匹配到就停止
System.out.println(persons.stream().anyMatch(item -> item.age == 18));
// 都是则返回true
System.out.println(persons.stream().allMatch(item -> item.age == 18));
// 都不是则返回true
System.out.println(persons.stream().noneMatch(item -> item.age == 18));
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
true
false
false
Object[] listToArrayobjects = persons.toArray();
System.out.println("listToArrayobjects = " + Arrays.toString(listToArrayobjects));
Person[] listToArrayPersons = persons.toArray(new Person[0]);
System.out.println("listToArrayPersons = " + Arrays.toString(listToArrayPersons));
Object[] streamToArrayObjects = persons.stream().filter(p -> p.age > 18).toArray();
System.out.println("streamToArrayObjects = " + Arrays.toString(streamToArrayObjects));
Person[] streamToArrayPersons = persons.stream().filter(p -> p.age > 18).toArray(Person[]::new);
System.out.println("streamToArrayPersons = " + Arrays.toString(streamToArrayPersons));
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
listToArrayobjects = [{name='Max', age=18}, {name='Peter', age=23}, {name='Pamela', age=23}, {name='David', age=12}]
listToArrayPersons = [{name='Max', age=18}, {name='Peter', age=23}, {name='Pamela', age=23}, {name='David', age=12}]
streamToArrayObjects = [{name='Peter', age=23}, {name='Pamela', age=23}]
streamToArrayPersons = [{name='Peter', age=23}, {name='Pamela', age=23}]
注意:使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组(阿里开发手册)
直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。
说明:使用 toArray 带参方法,数组空间大小的 length: 1) 等于 0,动态创建与 size 相同的数组,性能最好。 2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。 3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与上相同。 4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。
规约操作可以将流的所有元素组规约一个结果。Java 8 支持三种不同的reduce
方法。第一种将流中的元素规约成流中的一个元素。
找出年龄最大的人
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
persons.stream().reduce((p1,p2)->p1.age>p2.age?p1:p2).ifPresent(System.out::println);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
{name='Pamela', age=23}
第二种reduce
方法接受标识值和BinaryOperator
累加器。此方法可用于构造一个新的 Person
,其中包含来自流中所有其他人的聚合名称和年龄:
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
Person result = persons.stream().reduce(new Person("", 0), (p1, p2) -> {
p1.age += p2.age;
p1.name += p2.name;
return p1;
});
System.out.format("name=%s; age=%s", result.name, result.age);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
name=MaxPeterPamelaDavid; age=76
第三种reduce
方法接受三个参数:标识值,BiFunction
累加器和类型的组合器函数BinaryOperator
。由于初始值的类型不一定为Person
,我们可以使用这个归约函数来计算所有人的年龄总和:
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
Integer ageSum = persons.stream().reduce(0, (sum, p) -> sum += p.age, Integer::sum);
System.out.println(ageSum);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
76
结果为76,但是内部究竟发生了什么呢?让我们再打印一些调试日志:
Integer ageSum = persons.stream().reduce(0,
(sum, p) -> {
System.out.format("accumulator: sum=%s; person=%s\n", sum, p);
return sum += p.age;
},
(sum1, sum2) -> {
System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
return sum1 + sum2;
});
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
accumulator: sum=0; person={name='Max', age=18}
accumulator: sum=18; person={name='Peter', age=23}
accumulator: sum=41; person={name='Pamela', age=23}
accumulator: sum=64; person={name='David', age=12}
你可以看到,累加器函数完成了所有工作。它首先使用初始值0
和第一个人年龄相加。接下来的三步中sum
会持续增加,直到76。
等等?好像哪里不太对!组合器从来都没有调用过啊?
我们以并行流的方式运行上面的代码,看看日志输出:
Integer ageSum = persons.parallelStream().reduce(0,
(sum, p) -> {
System.out.format("accumulator: sum=%s; person=%s\n", sum, p);
return sum += p.age;
},
(sum1, sum2) -> {
System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
return sum1 + sum2;
});
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=12}
accumulator: sum=0; person={name='Max', age=18}
accumulator: sum=0; person={name='Peter', age=23}
combiner: sum1=18; sum2=23
accumulator: sum=0; person={name='Pamela', age=23}
accumulator: sum=0; person={name='David', age=12}
combiner: sum1=23; sum2=12
combiner: sum1=41; sum2=35
并行流的执行方式完全不同。这里组合器被调用了。实际上,由于累加器被并行调用,组合器需要被用于计算部分累加值的总和。
数据源
private static List<Person> getPersons() {
List<Person> persons = Arrays.asList(new Person("Max", 18),
new Person("Peter", 23),
new Person("Pamela", 23),
new Person("David", 12));
return persons;
}
如果有两个相同的key会报错java.lang.IllegalStateException: Duplicate key Peter
源码
public static <T, K, U > Collector < T, ?,Map<K, U>>toMap(
Function < ? super T, ? extends K > keyMapper,
Function < ? super T, ? extends U > valueMapper){
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
测试代码
// [{name='Max', age=18}, {name='Peter', age=23}, {name='Pamela', age=23}, {name='David', age=12}]
List<Person> persons = getPersons();
// 出现两个相同的key,报错
Map<Integer, String> map = persons.stream().collect(Collectors.toMap(p -> p.age, p -> p.name));
出现两个相同的key,将value合并
源码
public static <T, K, U > Collector < T, ?,Map<K, U>>toMap(
Function < ? super T, ? extends K > keyMapper,
Function < ? super T, ? extends U > valueMapper,
BinaryOperator < U > mergeFunction){
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
测试代码
BinaryOperator<String> mergeFunction = (value1, value2) -> {
if (value1.equals(value2)) {
return value1;
}
return value1 + ";" + value2;
};
Map<Integer, String> map = persons.stream().collect(Collectors.toMap(p -> p.age, p -> p.name, mergeFunction));
System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// 分组
Map<Integer, List<Person>> groupByPersons = persons.stream().collect(Collectors.groupingBy(p -> p.age));
System.out.println("groupingByPersons = " + groupByPersons);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=11}
groupingByPersons = {18=[{name='Max', age=18}], 23=[{name='Peter', age=23}, {name='Pamela', age=23}], 11=[{name='David', age=11}]}
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// 分区
Map<Boolean, List<Person>> partitioningByPersons = persons.stream().collect(Collectors.partitioningBy(p -> p.age >= 18));
System.out.println("partitioningByPersons = " + partitioningByPersons);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=11}
partitioningByPersons = {false=[{name='David', age=11}], true=[{name='Max', age=18}, {name='Peter', age=23}, {name='Pamela', age=23}]}
List<Person> persons = getPersons();
for (Person person : persons) {
System.out.println("person = " + person);
}
// 综合统计age属性 count max min sum average
IntSummaryStatistics statistics = persons.stream().collect(Collectors.summarizingInt(p -> p.age));
System.out.println("statistics = " + statistics);
person = {name='Max', age=18}
person = {name='Peter', age=23}
person = {name='Pamela', age=23}
person = {name='David', age=11}
statistics = IntSummaryStatistics{count=4, sum=75, min=11, average=18.750000, max=23}
count = 4
max = 23
min = 11
sum = 75
average = 18.75
如果能用
/**
* Can be replaced with 'String.join'
* 如果能用String.join则优先使用String.join 例如字符串数组或字符串集合
* 如果不能用String.join再用Collectors.joining(),例如对象的某个属性拼接(需要先map成字符串,然后收集)
*/
String collect = String.join("", list);
// Can be replaced with 'String.join'
collect = list.stream().collect(Collectors.joining());
System.out.println("collect = " + collect);
// Can be replaced with 'String.join'
collect = list.stream().collect(Collectors.joining("and"));
System.out.println("collect = " + collect);
// 底层实现StringJoiner,StringJoiner的底层实现StringBuilder
System.out.println("collect = " + collect);
String collect3 = list.stream().collect(Collectors.joining("and", "start", "end"));
System.out.println("collect3 = " + collect3);
‘collect(summingInt())’ can be replaced with ‘mapToInt().sum()’
对age属性求和或者平均数,可以先用mapToInt成IntStream,然后调用IntStream的sum,average方法而非收集器的方法
最大值Collectors.maxBy 最小值Collectors.minBy 规约Collectors.reducing 均推荐用stream的max,min,reduce
一个Collector是由四部分组成的:
Collector自定义起来,也不是特别的麻烦,不过要明确以下几点:
现在有个简单的需求,求一段数字的和,如果是奇数,直接相加;如果是偶数,乘以2后在相加。这样的场景下,Collector类库中的收集器不能满足我们的需求,我们只能够自己定义了。
class IntegerSum {
int sum;
public IntegerSum doSum(Integer item) {
if (item % 2 == 0) {
this.sum += item * 2;
} else {
this.sum += item;
}
return this;
}
public IntegerSum doCombine(IntegerSum it) {
this.sum += it.sum;
return this;
}
public Integer toValue() {
return this.sum;
}
}
public static void main(String[] args) {
// 1.创建新的结果容器
Supplier<IntegerSum> supplier = IntegerSum::new;
// 2.将元素添加到结果容器
BiConsumer<IntegerSum, Integer> accumulator = IntegerSum::doSum;
// 3.合并多个结果容器
BinaryOperator<IntegerSum> combiner = IntegerSum::doCombine;
// 4.最终结果的转换
Function<IntegerSum, Integer> finisher = IntegerSum::toValue;
/**
* 泛型说明
* T 待收集元素的类型
* A 累加器的类型
* R 最终结果的类型
*/
Collector<Integer, IntegerSum, Integer> collector = Collector.of(supplier, accumulator, combiner, finisher);
Integer result = Stream.of(1, 3, 5, 7).collect(collector);
System.out.println("result = " + result);
}
/**
* 泛型说明
* T 待收集元素的类型
* A 累加器的类型
* R 最终结果的类型
*/
Collector<Integer, IntegerSum, Integer> collector = new Collector<Integer, IntegerSum, Integer>() {
@Override
public Supplier<IntegerSum> supplier() {
return IntegerSum::new;
}
@Override
public BiConsumer<IntegerSum, Integer> accumulator() {
return IntegerSum::doSum;
}
@Override
public BinaryOperator<IntegerSum> combiner() {
return IntegerSum::doCombine;
}
@Override
public Function<IntegerSum, Integer> finisher() {
return IntegerSum::toValue;
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
};
Integer result = Stream.of(1, 3, 5, 7).collect(collector);
System.out.println("result = " + result);
enum Characteristics
流是可以并行执行的,当流中存在大量元素时,可以显著提升性能。并行流底层使用的ForkJoinPool
, 它由ForkJoinPool.commonPool()
方法提供。底层线程池的大小取决于 CPU 可用核心数:
ForkJoinPool commonPool = ForkJoinPool.commonPool();
System.out.println(commonPool.getParallelism()); // 11
在我的机器上,公共池初始化默认值为 3。你也可以通过设置以下JVM参数可以减小或增加此值:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
集合支持parallelStream()
方法来创建元素的并行流。或者你可以在已存在的数据流上调用中间方法parallel()
,将串行流转换为并行流,这也是可以的。
Arrays.asList("a1", "a2", "b1", "c2", "c1").parallelStream()
.filter(s -> {
System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName());
return true;
})
.map(s -> {
System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName());
return s.toUpperCase();
})
.sorted((s1, s2) -> {
System.out.format("sort: %s <> %s [%s]\n", s1, s2, Thread.currentThread().getName());
return s1.compareTo(s2);
})
.forEach(s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName()));
filter: b1 [main]
filter: c2 [ForkJoinPool.commonPool-worker-4]
map: c2 [ForkJoinPool.commonPool-worker-4]
filter: c1 [ForkJoinPool.commonPool-worker-11]
map: c1 [ForkJoinPool.commonPool-worker-11]
filter: a2 [ForkJoinPool.commonPool-worker-9]
map: a2 [ForkJoinPool.commonPool-worker-9]
filter: a1 [ForkJoinPool.commonPool-worker-2]
map: a1 [ForkJoinPool.commonPool-worker-2]
map: b1 [main]
sort: A2 <> A1 [main]
sort: B1 <> A2 [main]
sort: C2 <> B1 [main]
sort: C1 <> C2 [main]
sort: C1 <> B1 [main]
sort: C1 <> C2 [main]
forEach: B1 [main]
forEach: C1 [ForkJoinPool.commonPool-worker-9]
forEach: A1 [ForkJoinPool.commonPool-worker-11]
forEach: C2 [ForkJoinPool.commonPool-worker-4]
forEach: A2 [ForkJoinPool.commonPool-worker-2]
貌似sort
只在主线程上串行执行。但是实际上,并行流中的sort
在底层使用了Java8中新的方法Arrays.parallelSort()
。如 javadoc官方文档解释的,这个方法会按照数据长度来决定以串行方式,或者以并行的方式来执行。
如果指定数据的长度小于最小数值,它则使用相应的Arrays.sort
方法来进行排序。
总之,你需要记住的是,并行流对含有大量元素的数据流提升性能极大。但是你也需要记住并行流的一些操作,例如reduce
和collect
操作,需要额外的计算(如组合操作),这在串行执行时是并不需要。
此外,我们也了解了,所有并行流操作都共享相同的 JVM 相关的公共ForkJoinPool
。所以你可能需要避免写出一些又慢又卡的流式操作,这很有可能会拖慢你应用中,严重依赖并行流的其它部分代码的性能。
作者:犬小哈 链接:https://juejin.cn/post/6844903830254010381 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。