自从Java8推出之后,Stream新特性就被广泛关注,我在前几篇也着重介绍了一下,从编写代码角度确实要比之前干净、优雅得多,但是有一个问题一直有争议,那就是性能问题,用了Stream效率会不会降低?真的会出现网上所说的效率低20倍吗?网上测评文章也很多,莫衷于世,众说纷纭,这两天闲来无事,我也对此进行了测试:
测试机器
机器1:双核8G内存
机器2:四核16G内存
测试数据:一个有10000000个随机32位整数的ArrayList,实现代码如下:
List<Integer> cache = new ArrayList<>();
Random seed = new Random();
for (int i = 0; i < 10000000; i++) {
cache.add(seed.nextInt());
}
测试项目
项目1:对每个元素开平方根后求和;
常规方法:不用任何Java8的新特性完成任务
double total = 0;
for (Integer i : cache) {
total += Math.sqrt(i);
}
串行流:用stream流方式完成任务
double total = cache.stream().map(Math::sqrt).reduce(Double::sum).orElse(0.);
并行流:用parallelStream并行流完成任务
double total = cache.parallelStream().map(Math::sqrt).reduce(Double::sum).orElse(0.);
项目2:对每个元素开平方根后转存到另外一个ArrayList
// 常规方法
double total = 0;
List<Double> ret = new ArrayList<>();
for (Integer i : cache) {
ret.add(Math.sqrt(i));
}
// 串行流
List<Double> ret = cache.stream().map(Math::sqrt).collect(Collectors.toList());
// 并行流
List<Double> ret = cache.parallelStream().map(Math::sqrt).collect(Collectors.toList());
项目3:筛选出3的倍数然后转存到一个新的ArrayList
// 常规方法
List<Integer> ret = new ArrayList<>();
for (Integer i : cache) {
if (i % 3 == 0) {
ret.add(i);
}
}
// 串行流方式
List<Integer> ret = cache.stream().filter(i->i%3==0).collect(Collectors.toList());
// 并行流方式
List<Integer> ret = cache.parallelStream().filter(i->i%3==0).collect(Collectors.toList());
项目4:将所有相同元素进行分组并转存到Map<Integer, List<Integer>>
// 常规方法
Map<Integer, List<Integer>> group = new HashMap<>();
for (Integer i : cache) {
List<Integer> g = group.get(i);
if (g == null) {
g = new ArrayList<>();
}
g.add(i);
group.put(i, g);
}
// 串行流方式
cache.stream().collect(Collectors.groupingBy(Function.identity()));
// 并行流方式
cache.parallelStream().collect(Collectors.groupingBy(Function.identity()));
实施方案
为防止计算环境变化带来的不确定性,我们每个测试项目都执行100次,然后统计每次执行时间,单位秒。
测试结果
机器1执行情况:

机器1:项目1执行情况

机器1:项目2执行情况

机器1:项目3执行情况

机器1:项目4执行情况
机器1执行情况说明:总体来看,好像串行流表现比较差,我们看看下图平均值

机器1各项目执行平均值比较
情况是不是好很多呢,stream并没有网传的那么糟糕,常规方法也就在项目1中表现优异
***特别说一下:在执行项目4时,由于我的机器1(一台笔记本)性能较差,执行时间太长了,所以没有执行100次,就执行了10次。另外,如果按照默认JVM内存设置,并行流执行项目4时直接爆OOM异常!因此有理由认为并行流还是比较耗费资源的,童鞋们在使用时要格外注意。
机器2执行情况:

机器2项目1执行情况

机器2项目2执行情况

机器2项目3执行情况

机器2项目4执行情况

机器2各项目执行平均值
机器2的执行情况可以看出,常规方法并没有多少优势,而且机器机器越好,串行流与常规方法的差距也变得可以忽略不计了。
总结陈词
1、但凡新事物出现,必然会有支持者和反对者,这是符合辩证的,我们一定不要道听途说,人云亦云,绝知此事要躬行啊,老祖宗教导的没错。至于童鞋们喜欢或习惯于哪种方式,取决于你们,但有一条,就是我们不能排斥新事物,对于新事物我们需要拥抱它、了解它、熟悉它,这样我们才能不断进步;
2、三种实现方式其实差别并不大,只用其中一种并不会造成很大影响,如果对于性能有苛刻要求,我们可以酌情选择,不必拘泥于形式。从前面测试我们可以看出,并行计算在绝大多数情况下表现是优秀的,但在项目4的执行情况不管哪台机器并行流表现都是最差的,我判断原因应该是虽然是并行处理,但是分组本身这个操作在并行计算完毕之后,还需要合并,这应该是导致效率较差的主要原因。因此,并行流处理在当分块计算之间如果没有任何关联时效率总是最高的。
3、一定要用发展的眼光去看问题,Java之所以推出这么多新特性,一定是有其必然性和必要性,反正我是肯定比不上这些计算机专家,因此只要有利于我们提高产出效率又可以让我们写的代码干净纯粹,我们何乐而不为呢?
| 留言与评论(共有 0 条评论) |