自从Java8推出之后,Stream新特性就被广泛关注,我在前几篇也着重介绍了一下,从编写程式码角度确实要比之前干净、优雅得多,但是有一个问题一直有争议,那就是效能问题,用了Stream效率会不会降低?真的会出现网上所说的效率低20倍吗?网上测评文章也很多,莫衷于世,众说纷纭,这两天闲来无事,我也对此进行了测试:
测试机器
机器1:双核8G内存
机器2:四核16G内存
测试资料:一个有10000000个随机32位整数的ArrayList,实现程式码如下:
List cache = new ArrayList();
Random seed = new Random();
for (int i = 0; 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 ret = new ArrayList();
for (Integer i : cache) {
ret.add(Math.sqrt(i));
}
// 序列流
List ret = cache.stream().map(Math::sqrt).collect(Collectors.toList());
// 并行流
List ret = cache.parallelStream().map(Math::sqrt).collect(Collectors.toList());
专案3:筛选出3的倍数然后转存到一个新的ArrayList
// 常规方法
List ret = new ArrayList();
for (Integer i : cache) {
if (i % 3 == 0) {
ret.add(i);
}
}
// 序列流方式
List ret = cache.stream().filter(i->i%3==0).collect(Collectors.toList());
// 并行流方式
List ret = cache.parallelStream().filter(i->i%3==0).collect(Collectors.toList());
专案4:将所有相同元素进行分组并转存到Map>
// 常规方法
Map> group = new HashMap();
for (Integer i : cache) {
List 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之所以推出这么多新特性,一定是有其必然性和必要性,反正我是肯定比不上这些计算机专家,因此只要有利于我们提高产出效率又可以让我们写的程式码干净纯粹,我们何乐而不为呢?