在Java 8中引入了Stream API,我们在对集合进行操作时可以运用函数式编程的思想,用户只需要考虑“做什么”,而不需要考虑“怎么做”,函数式编程固然是简洁的,那么与传统的循环结构相比较,Stream API能不能在简洁的同时提高效率呢?
测试方法
testTime方法使用Runnable接口,利用System.nanoTime方法计算程序运行时间
public static void testTime(Runnable runnable) {
long stime = System.nanoTime();
runnable.run();
long etime = System.nanoTime();
System.out.println("time" + (etime - stime));
}
用于测试的集合存入一组随机数
ArrayList<Double> list1 = new ArrayList<>();
for (int i = 0; i < 10e7; ++i) {
list1.add(Math.random());
}
对于每一种遍历结构,使用testTime方法测试时间,将每次遍历得到的值打印出来
testTime(() -> {
for (int i = 0; i < list1.size(); ++i) {
System.out.println(list1.get(i));
}
});
testTime(() -> {
for (Double d : list1) {
System.out.println(d);
}
});
testTime(() -> list1.forEach(System.out::println));
testTime(() -> list1.stream().forEach(System.out::println));
testTime(() -> list1.parallelStream().forEach(System.out::println));
同时为了防止jvm优化加入-XX:CompileThreshold=0 -XX:+TieredCompilation参数关闭jvm优化
测试结果
因为实际环境的影响,测试的结果只是一个大概的范围,并不准确,不过仍然具有一定的参考价值,以下是不同数据量的测试结果
单位:毫秒(ms) | 1K | 1W | 10W | 100W | 1000W |
---|---|---|---|---|---|
for | 13.79 | 38.84 | 198.35 | 1404.31 | 13107.81 |
forEach | 3.92 | 21.56 | 150.82 | 1274.32 | 12771.57 |
ArrayList.forEach | 3.90 | 21.32 | 163.45 | 1302.86 | 12619.82 |
stream.forEach | 3.10 | 16.80 | 144.16 | 1298.58 | 12678.98 |
parallelStream.forEach | 6.74 | 27.52 | 214.81 | 1758.13 | 17271.42 |
其实这几种形式的效率和数据量有关系,在数据量较少时(0-100W),传统的for循环和foreach效率都是比较高的,而在数据量较大时(接近1000W),这时使用StreamAPI的效率就要远远大于传统循环了,特别的,因为Stream是串行操作,对于数据量巨大时,可以使用parallelStream(异步多线程),但同时也要考虑到并行带来的后果(如数据是否有序,是否线程安全等等),这也就是为什么在本例中parallelStream的速度远不如其他的原因(System.out.println是一个线程安全方法)。
总之,在一般的场景中,for循环以及foreach循环足够使用,并且性能很不错,只有当数据量巨大时,StreamAPI才是性能角度更优的选择,当然,如果你喜欢更简洁的代码逻辑,使用StreamAPI有何不可呢?