在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)1K1W10W100W1000W
for13.7938.84198.351404.3113107.81
forEach3.9221.56150.821274.3212771.57
ArrayList.forEach3.9021.32163.451302.8612619.82
stream.forEach3.1016.80144.161298.5812678.98
parallelStream.forEach6.7427.52214.811758.1317271.42

其实这几种形式的效率和数据量有关系,在数据量较少时(0-100W),传统的for循环和foreach效率都是比较高的,而在数据量较大时(接近1000W),这时使用StreamAPI的效率就要远远大于传统循环了,特别的,因为Stream是串行操作,对于数据量巨大时,可以使用parallelStream(异步多线程),但同时也要考虑到并行带来的后果(如数据是否有序,是否线程安全等等),这也就是为什么在本例中parallelStream的速度远不如其他的原因(System.out.println是一个线程安全方法)。

总之,在一般的场景中,for循环以及foreach循环足够使用,并且性能很不错,只有当数据量巨大时,StreamAPI才是性能角度更优的选择,当然,如果你喜欢更简洁的代码逻辑,使用StreamAPI有何不可呢?