Performance: Java's String.format

19 浏览
0 Comments

Performance: Java's String.format

在Java应用程序中,是否使用String.format而不是StringBuilder是一个好的选择?所以,我写了一个简单的测试,如下所示:

public static void main(String[] args) {
    int i = 0;
    Long start = System.currentTimeMillis();
    while (i < 10000) {
        String s = String.format("test %d", i);
        i++;
    }
    System.out.println(System.currentTimeMillis() - start);
    i = 0;
    start = System.currentTimeMillis();
    while (i < 10000) {
        String s = new StringBuilder().append("test ").append(i).toString();
        i++;
    }
    System.out.println(System.currentTimeMillis() - start);
}

结果如下:

238
15

因此,如果我的测试有效,StringBuilder比String.format更快。好的。

现在,我开始思考String.format是如何工作的。它是一个简单的字符串拼接,就像`"test " + i`一样吗?

StringBuilder的拼接和String.format有什么区别?有没有一种像String.format一样简单且像StringBuilder一样快的方式?

0
0 Comments

Performance: Java的String.format

在这个问题中,出现的原因是要比较String.format()和StringBuilder、StringBuffer、普通的String +操作符、String.replace()和String.concat()方法之间的性能差异。下面是一个使用caliper基准测试编写的快速示例代码:

public class StringFormatBenchmark extends SimpleBenchmark {
    public void timeStringFormat(int reps) {
        while (--reps >= 0) {
            String s = String.format("test %d", reps);
        }
    }
    public void timeStringBuilder(int reps) {
        while (--reps >= 0) {
            String s = new StringBuilder("test ").append(reps).toString();
        }
    }
    public void timeStringBuffer(int reps) {
        while (--reps >= 0) {
            String s = new StringBuffer("test ").append(reps).toString();
        }
    }
    public void timeStringPlusOperator(int reps) {
        while (--reps >= 0) {
            String s = "test " + reps;
        }
    }
    public void timeReplace(int reps) {
        while (--reps >= 0) {
            String s = "test {}".replace("{}", String.valueOf(reps));
        }
    }
    public void timeStringConcat(int reps) {
        while (--reps >= 0) {
            String s = "test ".concat(String.valueOf(reps));
        }
    }
    public static void main(String[] args) {
        new Runner().run(StringFormatBenchmark.class.getName());
    }
}

以下是结果(Java 1.6.0_26-b03,Ubuntu,32位):

![caliper2](https://i.stack.imgur.com/Hh5Gr.png)

显然,String.format()要慢得多(慢上一个数量级)。此外,StringBuffer比StringBuilder要慢得多(正如我们所学到的)。最后,StringBuilder和String +操作符几乎相同,因为它们编译为非常相似的字节码。String.concat()稍微慢一些。

如果只需要简单的连接,也不要使用String.replace()。

如果特殊情况下只有两个连接操作数,则最好将"test ".concat(String.valueOf(reps))添加到基准测试中,因为它更有可能编译成这种形式(对于单个操作没有构建器的好处)。

谢谢,测试非常好。

所以我做了,谢谢你的建议,我更新了我的基准测试和结果。随时在你的计算机上运行测试。

我在我的计算机上运行了它(Java 7,OS X),concat和StringBuilder之间的差距甚至更大。这让我感到惊讶,因为concat表面上做的工作更少(例如,只有一个char[]分配)。在这种情况下,+运算符甚至使用StringBuilder进行编译(当然,这取决于使用的编译器;我使用的是Eclipse的编译器)。

从你所创建的图片中,不要草率得得出结论!你的测试只测量了一个字符串操作的时间!(循环只是为了测试准确性。)如果在循环中构建字符串,StringBuffer和StringBuilder比+运算符要快得多。例如,结果可能是:+运算符在构建500000个字符的字符串时可能需要超过一分钟的时间,而StringBuilder可以在50毫秒以内构建完全相同的字符串!

这个基准测试可以完全优化掉,因为字符串永远不会被使用。将它们的长度相加将有所帮助。

是的,但是你可以像这样使用builder.append(String.format(...))来同时获得可读性和一些速度。在循环中追加字符串确实不是一个选择。

0
0 Comments

在上述代码中,有两个方法(method1和method2),它们分别使用了不同的方式来拼接字符串。通过运行测试,发现使用StringBuilder拼接字符串的方法比使用String.format方法快了30倍以上。

问题的原因是String.format方法的性能较低。String.format方法是通过格式化字符串来生成新的字符串,这个过程需要进行多次操作,包括字符串的拼接和格式化。而StringBuilder则是一种高效的字符串拼接方法,它使用了可变长度的字符数组来存储字符串,避免了频繁的字符串拼接操作。

解决方法是使用StringBuilder来代替String.format方法进行字符串拼接。这样可以大大提高代码的性能,减少了不必要的字符串操作。

下面是修改后的代码:

public class TestPerf {
    private static int NUM_RUN;
    public static void main(String[] args) {
        NUM_RUN = 100_000;
        //warm up
        for (int i = 0; i < 10; i++) {
            method1();
            method2();
        }
        System.gc();
        System.out.println("Starting");
        long sum = 0;
        long start = System.nanoTime();
        for (int i = 0; i < 10; i++) {
            sum += method1();
        }
        long end = System.nanoTime();
        System.out.println("format: " + (end - start) / 1000000);
        System.gc();
        start = System.nanoTime();
        for (int i = 0; i < 10; i++) {
            sum += method2();
        }
        end = System.nanoTime();
        System.out.println("stringbuilder: " + (end - start) / 1000000);
        System.out.println(sum);
    }
    private static int method1() {
        int sum = 0;
        for (int i = 0; i < NUM_RUN; i++) {
            String s = new StringBuilder().append("test ").append(i).toString();
            sum += s.length();
        }
        return sum;
    }
    private static int method2() {
        int sum = 0;
        for (int i = 0; i < NUM_RUN; i++) {
            String s = String.format("test %d", i);
            sum += s.length();
        }
        return sum;
    }
}

0
0 Comments

原因:String.format相对较慢,但通常足够快。如果使用String.format更简单,除非在分析应用程序时发现性能问题,我会使用format。在示例中,String.format花费了大约24微秒,并且还没有完全预热。忽略前10000次迭代。

解决方法:在这种情况下,我认为"test " + i是最简单的方法。

0