list拼接引发的思考

一. 需求描述

有一个list,我需要将list中的内容拼接起来,以逗号为间隔符。

二.实现方案

    1. for 循环拼接
    1. java stream api: reduce方法(非并行)

三.方案对比

测试代码:

public static void test002() {
        ArrayList aList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            aList.add(""+i);
        }
        System.out.println("size"+aList.size());
        long start1 = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < aList.size()-1; i++) {
            sb.append(aList.get(i));
            sb.append(",");
        }
        String s1 = sb.append(aList.get(aList.size() - 1)).toString();
        long end1 = System.currentTimeMillis();
        System.out.println("for:"+(end1-start1));

        long start2 = System.currentTimeMillis();
        String s = aList.stream().reduce((last, next) -> {
            return last + "," + next;
        }).get();
        long end2 = System.currentTimeMillis();
        System.out.println("reduce:"+(end2-start2));
    }

测试结果:

size:10000
for:4
reduce:406

四.测试分析

作为后来添加的api,reduce方法无疑编写更快捷,结构流程更直观。但性能却较差。
分析原因如下:
在for循环中,我们只使用了外部已经定义好的一个StringBuilder来进行拼接。
而在reduce中,尽管java中字符串使用"+"号底层也是用StringBuilder来进行处理,但是此StringBuilder在reduce每一次一次执行中都会重新生成,进行了多少次"+",就会有多少个StringBuilder生成的过程。因此在此,reduce性能较差。

思考:

如果我们list中是int,reduce进行int的相加性能如何?

五.使用int相加做测试

private static void test004() {
        ArrayList aList = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            aList.add(i);
        }
        System.out.println("size:"+aList.size());
        long start1 = System.currentTimeMillis();
        int sum=0;
        for (int i = 0; i < aList.size(); i++) {
            sum = sum+aList.get(i);
        }
        System.out.println(sum);
        long end1 = System.currentTimeMillis();
        System.out.println("for:"+(end1-start1));

        long start2 = System.currentTimeMillis();
        Integer s = aList.stream().reduce((last, next) -> {
            return last + next;
        }).get();
        System.out.println(s);
        long end2 = System.currentTimeMillis();
        System.out.println("reduce:"+(end2-start2));
    }

测试结果:

size:10000
49995000
for:2
49995000
reduce:100

结果分析:

按之前的想法,此处应该性能相差不多,但实际上性能依旧相差很大。因此我们初步分析有一定道理,但并不是核心原因。进一步推测,可能是在进行stream流转换的时候产生的性能差异。


最后

参考了网上的文章:https://www.cnblogs.com/CarpenterLee/p/6675568.html
此文章做了更复杂和详细的测试,并做了如下总结:

- 对于简单操作,比如最简单的遍历,Stream串行API性能明显差于显示迭代,但并行的Stream API能够发挥多核特性。
- 对于复杂操作,Stream串行API性能可以和手动实现的效果匹敌,在并行执行时Stream API效果远超手动实现。

所以,如果出于性能考虑,

- 1. 对于简单操作推荐使用外部迭代手动实现,
- 2. 对于复杂操作,推荐使用Stream API,
- 3. 在多核情况下,推荐使用并行Stream API来发挥多核优势,
- 4.单核情况下不建议使用并行Stream API。

如果出于代码简洁性考虑,使用Stream API能够写出更短的代码。即使是从性能方面说,尽可能的使用Stream API也另外一个优势,那就是只要Java Stream类库做了升级优化,代码不用做任何修改就能享受到升级带来的好处。

你可能感兴趣的:(list拼接引发的思考)