关于stream:JDK8-Stream性能测试看看到底有多快

6次阅读

共计 5377 个字符,预计需要花费 14 分钟才能阅读完成。

送大家以下学习材料,文末有支付形式




JDK8 Stream 数据流效率剖析

Stream 是 Java SE 8 类库中新增的要害形象,它被定义于 java.util.stream(这个包里有若干流类型:Stream<T> 代表对象援用流。

此外还有一系列特化流,如 IntStream,LongStream,DoubleStream 等),Java 8 引入的的 Stream 次要用于取代局部 Collection 的操作,每个流代表一个值序列,流提供一系列罕用的汇集操作,能够便捷的在它下面进行各种运算。

汇合类库也提供了便捷的形式使咱们能够以操作流的形式应用汇合、数组以及其它数据结构;

stream 的操作品种

①两头操作

  • 当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“两头操作”;
  • 两头操作依然会返回一个流对象,因而多个两头操作能够串连起来造成一个流水线;
  • stream 提供了多种类型的两头操作,如 filter、distinct、map、sorted 等等;

②终端操作

  • 当所有的两头操作实现后,若要将数据从流水线上拿下来,则须要执行终端操作;
  • stream 对于终端操作,能够间接提供一个两头操作的后果,或者将后果转换为特定的 collection、array、String 等;

stream 的特点

①只能遍历一次:

数据流的从一头获取数据源,在流水线上顺次对元素进行操作,当元素通过流水线,便无奈再对其进行操作,能够从新在数据源获取一个新的数据流进行操作;

②采纳外部迭代的形式:

对 Collection 进行解决,个别会应用 Iterator 遍历器的遍历形式,这是一种 内部迭代;

而对于解决 Stream,只有申明解决形式,处理过程由流对象自行实现,这是一种 外部迭代,对于大量数据的迭代解决中,外部迭代比内部迭代要更加高效;

stream 绝对于 Collection 的长处

  • 无存储:流并不存储值;流的元素源自数据源(可能是某个数据结构、生成函数或 I / O 通道等等),通过一系列计算步骤失去;
  • 函数式格调:对流的操作会产生一个后果,但流的数据源不会被批改;
  • 惰性求值:少数流操作(包含过滤、映射、排序以及去重)都能够以惰性形式实现。这使得咱们能够用一遍遍历实现整个流水线操作,并能够用短路操作提供更高效的实现;
  • 无需上界:不少问题都能够被表白为有限流(infinite stream):用户不停地读取流直到称心的后果呈现为止(比如说,枚举 完满数 这个操作能够被表白为在所有整数上进行过滤);汇合是无限的,但流能够表白为无线流;
  • 代码简练:对于一些 collection 的迭代解决操作,应用 stream 编写能够非常简洁,如果应用传统的 collection 迭代操作,代码可能非常啰嗦,可读性也会比拟蹩脚;

stream 和 iterator 迭代的效率比拟

好了,下面 stream 的长处吹了那么多,stream 函数式的写法是很难受,那么 steam 的效率到底怎么呢?

先说论断:

– 传统 iterator (for-loop) 比 stream(JDK8) 迭代性能要高,尤其在小数据量的状况下;

– 在多核情景下,对于大数据量的解决,parallel stream 能够有比 iterator 更高的迭代解决效率;

我别离对一个随机数列 List(数量从 10 到 10000000)进行映射、过滤、排序、规约统计、字符串转化场景下,对应用 stream 和 iterator 实现的运行效率进行了统计,。

测试环境如下:

System:Ubuntu 16.04 xenial

CPU:Intel Core i7-8550U

RAM:16GB

JDK version:1.8.0\_151

JVM:HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

JVM Settings:

-Xms1024m

-Xmx6144m

-XX:MaxMetaspaceSize=512m

-XX:ReservedCodeCacheSize=1024m

-XX:+UseConcMarkSweepGC

-XX:SoftRefLRUPolicyMSPerMB=100

1. 映射解决测试

把一个随机数列(List<Integer>)中的每一个元素自增 1 后,从新组装为一个新的 List<Integer>,测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x -> x)`
`.map(x -> ++x)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>();`
`for(Integer e : list){`
 `result.add(++e);`
`}`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x -> x)`
`.map(x -> ++x)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

2. 过滤解决测试

取出一个随机数列(List<Integer>)中的大于 200 的元素,并组装为一个新的 List<Integer>,测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x -> x)`
`.filter(x -> x > 200)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>(list.size());`
`for(Integer e : list){`
 `if(e > 200){`
 `result.add(e);`
 `}`
`}`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x -> x)`
`.filter(x -> x > 200)`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

3. 天然排序测试

对一个随机数列(List<Integer>)进行天然排序,并组装为一个新的 List<Integer>,iterator 应用的是 Collections # sort API(应用归并排序算法实现),测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`List<Integer> result = list.stream()`
`.mapToInt(x->x)`
`.sorted()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`List<Integer> result = new ArrayList<>(list);`
`Collections.sort(result);`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.mapToInt(x->x)`
`.sorted()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

4. 归约统计测试

获取一个随机数列(List<Integer>)的最大值,测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`int max = list.stream()`
`.mapToInt(x -> x)`
`.max()`
`.getAsInt();`
`//iterator`
`int max = -1;`
`for(Integer e : list){`
 `if(e > max){`
 `max = e;`
 `}`
`}`
`//parallel stream`
`int max = list.parallelStream()`
`.mapToInt(x -> x)`
`.max()`
`.getAsInt();`

5. 字符串拼接测试

获取一个随机数列(List<Integer>)各个元素应用“,”分隔的字符串,测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`String result = list.stream().map(String::valueOf).collect(Collectors.joining(","));`
`//iterator`
`StringBuilder builder = new StringBuilder();`
`for(Integer e : list){`
 `builder.append(e).append(",");`
`}`
`String result = builder.length() == 0 ? "" : builder.substring(0,builder.length() - 1);`
`//parallel stream`
`String result = list.stream().map(String::valueOf).collect(Collectors.joining(","));`

6. 混合操作测试

对一个随机数列(List<Integer>)进行去空值,除重,映射,过滤,并组装为一个新的 List<Integer>,测试的随机数列容量从 10 – 10000000,跑 10 次取均匀工夫;

`//stream`
`List<Integer> result = list.stream()`
`.filter(Objects::nonNull)`
`.mapToInt(x -> x + 1)`
`.filter(x -> x > 200)`
`.distinct()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`
`//iterator`
`HashSet<Integer> set  = new HashSet<>(list.size());`
`for(Integer e : list){`
 `if(e != null && e > 200){`
 `set.add(e + 1);`
 `}`
`}`
`List<Integer> result = new ArrayList<>(set);`
`//parallel stream`
`List<Integer> result = list.parallelStream()`
`.filter(Objects::nonNull)`
`.mapToInt(x -> x + 1)`
`.filter(x -> x > 200)`
`.distinct()`
`.boxed()`
`.collect(Collectors.toCollection(ArrayList::new));`

试验后果总结

从以上的试验来看,能够总结处以下几点:

  • 在少低数据量的解决场景中(size<=1000),stream 的解决效率是不如传统的 iterator 内部迭代器处理速度快的,然而实际上这些解决工作自身运行工夫都低于毫秒,这点效率的差距对一般业务简直没有影响,反而 stream 能够使得代码更加简洁;
  • 在大数据量(szie>10000)时,stream 的解决效率会高于 iterator,特地是应用了并行流,在 cpu 恰好将线程调配到多个外围的条件下(当然 parallel stream 底层应用的是 JVM 的 ForkJoinPool,这货色调配线程自身就很玄学),能够达到一个很高的运行效率,然而理论一般业务个别不会有须要迭代高于 10000 次的计算;
  • Parallel Stream 受引 CPU 环境影响很大,当没调配到多个 cpu 外围时,加上援用 forkJoinPool 的开销,运行效率可能还不如一般的 Stream;

应用 Stream 的倡议

  • 简略的迭代逻辑,能够间接应用 iterator,对于有多步解决的迭代逻辑,能够应用 stream,损失一点简直没有的效率,换来代码的高可读性是值得的
  • 单核 cpu 环境,不举荐应用 parallel stream,在多核 cpu 且有大数据量的条件下,举荐应用 paralle stream;
  • stream 中含有装箱类型,在进行两头操作之前,最好 转成对应的数值流,缩小因为频繁的拆箱、装箱造成的性能损失;

源于:blog.csdn.net/Al\_assad/article/details/82356606

正文完
 0