20

我目前正在研究 String concat 选项以及它们对整体性能的影响。我的测试用例产生的结果让我大吃一惊,我不确定我是否忽略了某些东西。

这是交易:"something"+"somethingElse"在java中执行将(在编译时)StringBuilder每次完成时都会创建一个新的。

对于我的测试用例,我正在从我的 HDD 加载一个包含1661 行示例数据的文件(经典的“Lorem Ipsum”)。这个问题不是关于 I/O 性能,而是关于不同字符串 concat 方法的性能。

public class InefficientStringConcat {

    public static void main(String[] agrs) throws Exception{
        // Get a file with example data:

        System.out.println("Starting benchmark");
        // Read an measure:
        for (int i = 0; i < 10; i++){
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(new FileInputStream(new File("data.txt")))
            );

            long start = System.currentTimeMillis();
            // Un-comment method to test:
            //inefficientRead(in);
            //betterRead(in);
            long end = System.currentTimeMillis();
            System.out.println("Took "+(end-start)+"ms");

            in.close();
        }



    }

    public static String betterRead(BufferedReader in) throws IOException{
        StringBuilder b = new StringBuilder();
        String line;
        while ((line = in.readLine()) != null){
            b.append(line);
        }
        return b.toString();
    }

    public static String inefficientRead(BufferedReader in) throws IOException {
        String everything = "", line;
        while ((line = in.readLine()) != null){
            everything += line;
        }
        return everything;
    }
}

如您所见,两个测试的设置相同。结果如下:

使用inefficientRead()- 方法

Starting benchmark
#1 Took 658ms
#2 Took 590ms
#3 Took 569ms
#4 Took 567ms
#5 Took 562ms
#6 Took 570ms
#7 Took 563ms
#8 Took 568ms
#9 Took 560ms
#10 Took 568ms

使用betterRead()-方法

Starting benchmark
#1 Took 42ms
#2 Took 10ms
#3 Took 5ms
#4 Took 7ms
#5 Took 16ms
#6 Took 3ms
#7 Took 4ms
#8 Took 5ms
#9 Took 5ms
#10 Took 13ms

我正在运行没有额外参数的测试 -java命令。我正在运行2009 年初的 MacMini3,1和 Sun JDK 7:

[luke@BlackBox ~]$ java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) Client VM (build 23.5-b02, mixed mode)

这让我觉得有很大的不同。我在衡量这个时做错了什么,或者这应该发生吗?

4

2 回答 2

25

我在衡量这个时做错了什么,或者这应该发生吗?

它应该发生。使用重复的字符串连接构造一个长字符串是一种已知的性能反模式:每个连接都必须创建一个新字符串,其中包含原始字符串的副本以及附加字符串的副本。你最终会得到 O(N 2 ) 的性能。当您使用 时StringBuilder,大多数时候您只是将附加字符串复制到缓冲区中。有时缓冲区需要用完空间并需要扩展(通过将现有数据复制到新缓冲区中),但这并不经常发生(由于缓冲区扩展策略)。

有关详细信息,请参阅我关于字符串连接的文章 - 这是一篇非常古老的文章,所以早于StringBuilder,但基本原理没有改变。(基本上StringBuilder就像StringBuffer,但没有同步。)

于 2013-03-02T18:42:19.660 回答
6

这正是应该发生的事情。 betterRead需要线性时间;inefficientRead需要二次时间。

于 2013-03-02T18:42:05.220 回答