连接运算符(+)与concat()

时间:2012-01-06 08:11:12

标签: java string-concatenation

对于字符串连接,我们可以使用concat()或concat运算符(+)

我尝试了以下性能测试,发现concat()更快,并且是一种内存有效的字符串连接方式。

字符串连接比较100,000次

String str = null;

//------------Using Concatenation operator-------------
long time1 = System.currentTimeMillis();
long freeMemory1 = Runtime.getRuntime().freeMemory();

for(int i=0; i<100000; i++){
    str = "Hi";
    str = str+" Bye";
}
long time2 = System.currentTimeMillis();
long freeMemory2 = Runtime.getRuntime().freeMemory();

long timetaken1 = time2-time1;
long memoryTaken1 = freeMemory1 - freeMemory2;
System.out.println("Concat operator  :" + "Time taken =" + timetaken1 +
                   " Memory Consumed =" + memoryTaken1);

//------------Using Concat method-------------
long time3 = System.currentTimeMillis();
long freeMemory3 = Runtime.getRuntime().freeMemory();
for(int j=0; j<100000; j++){
    str = "Hi";
    str = str.concat(" Bye");
}
long time4 = System.currentTimeMillis();
long freeMemory4 = Runtime.getRuntime().freeMemory();
long timetaken2 = time4-time3;
long memoryTaken2 = freeMemory3 - freeMemory4;
System.out.println("Concat method  :" + "Time taken =" + timetaken2 +
                   " Memory Consumed =" + memoryTaken2);

结果

Concat operator: Time taken = 31; Memory Consumed = 2259096
Concat method  : Time taken = 16; Memory Consumed = 299592

如果concat()比运算符更快,那么我们何时应该使用连接运算符(+)

8 个答案:

答案 0 :(得分:36)

concat方法总是生成一个带有连接结果的新String。

plus运算符由StringBuilder创建支持,附加所需的所有String值,并进一步调用toString()。

因此,如果你需要连接两个值,concat()将是更好的选择。如果需要连接100个值,则应使用加号运算符或显式使用StringBuilder(例如,如果在循环中追加)。

答案 1 :(得分:12)

实际上s1 + s2和s1.concat(s2)非常不同。

s1 + s2由javac转换为

(new StringBuilder(String.valueOf(s1)).append(s2).toString();

如果你反编译.class你可以看到它。这种结构效率不高;它涉及最多三个新的char []分配和三个char []复制操作。

s1.concat(s2)总是一个新的char [] +一个复制操作,参见String.java

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    char buf[] = new char[count + otherLen];
    getChars(0, count, buf, 0);
    str.getChars(0, otherLen, buf, count);
    return new String(0, count + otherLen, buf);
}

请注意,新的String(int,int,char [])是String的包私有构造函数。它直接使用char buf [],没有通常的复制,以确保字符串不变性的buf不可见性。

答案 2 :(得分:7)

您的测试需要运行至少2秒,每个循环在一个单独的方法中才有意义。短期测试可能难以重现和比较。从您的计时看来,您正在使用Windows(即因为您的时间是16和31毫秒;)请尝试使用System.nanoTime()。当你的循环迭代超过10,000次时,整个方法被编译。这意味着您的后一个方法在启动时已经被编译。

在回答你的问题时,在添加两个字符串时,concat会略微加快。但是,它带有打字和概念开销,可能比您节省的CPU大得多。即使你的测试重复100,000次,它也可以节省不到15毫秒的时间,而且它的成本远远超过你的时间(这可能是更值得的)你可以在未来版本的JVM中找到,差异总是得到优化,代码的复杂性仍然存在。


编辑:我没有注意到内存结果是可疑的。

String str = null;

//------------Using Concatenation operator-------------
long time1 = System.currentTimeMillis();
long freeMemory1 = Runtime.getRuntime().freeMemory();
for (int i = 0; i < 10000; i++) {
    str = "Hi";
    str = str + " Bye";
}
long time2 = System.currentTimeMillis();
long freeMemory2 = Runtime.getRuntime().freeMemory();

long timetaken1 = time2 - time1;
long memoryTaken1 = freeMemory1 - freeMemory2;
System.out.println("Concat operator  :" + "Time taken =" + timetaken1 + " Memory Consumed= " + memoryTaken1);

str = null;
//------------Using Concat method-------------
long time3 = System.currentTimeMillis();
long freeMemory3 = Runtime.getRuntime().freeMemory();
for (int j = 0; j < 10000; j++) {
    str = "Hi";
    str = str.concat(" Bye");

}
long time4 = System.currentTimeMillis();
long freeMemory4 = Runtime.getRuntime().freeMemory();

long timetaken2 = time4 - time3;
long memoryTaken2 = freeMemory3 - freeMemory4;
System.out.println("Concat method  :" + "Time taken =" + timetaken2 + " Memory Consumed= " + memoryTaken2);
使用-XX:-UseTLAB -mx1g

运行时打印

Concat operator  :Time taken =12 Memory Consumed= 1291456
Concat method  :Time taken =7 Memory Consumed= 560000

使内存使用率约为2:1。在原始问题中,每次运行时结果都会有所不同,有时.concat()似乎会使用更多。

答案 3 :(得分:3)

我相信连接的“风格”会有所作为。

对于concat(),它在内部创建一个新的char数组缓冲区,并根据该char数组返回一个新字符串。

对于+运算符,编译器实际上将其转换为使用StringBuffer / StringBuilder。

因此,如果你连接两个字符串,concat()绝对是一个更好的选择,因为创建的对象数只是结果String(和里面使用的char缓冲区),而使用+运算符将译成:

result = strA + strB;
-- translate to -->
result = new StringBuilder(strA).append(strB).toString();

创建了一个额外的StringBuilder实例。

但是,如果要连接,例如连续五个字符串,每个concat()将创建一个新的String对象。在使用+运算符时,编译器会将语句转换为具有多个追加操作的StringBuilder。它肯定会节省大量不必要的临时对象实例:

result = strA + strB + strC + strD + strE;
-- translate to -->
result = new StringBuilder(strA).append(strB).append(strC).append(strD).append(strE).toString();

答案 4 :(得分:0)

如果只使用&gt; = Java 1.5并且声明你的基本字符串(你想要的话),你可以始终使用+连接)循环之外。在Java 1.5中,它会导致创建new StringBuilder并对其进行处理直到字符串完成。这是最快的方式。

无论如何 - 如果你处于循环中(并用+连接字符串) - 循环的每次迭代都会创建new StringBuilder - 这不是最好的主意。因此,您应该强制使用StringBuilderStringBuffer(线程安全)类。

通常,此链接会清楚地回答您的问题,并为您提供完整的知识:

http://littletutorials.com/2008/07/16/stringbuffer-vs-stringbuilder-performance-comparison/

答案 5 :(得分:0)

虽然操作员和方法都提供相同的输出,但它们在内部工作的方式不同。

将str1与str2连接并输出字符串的concat()方法对于少量连接更有效。

但是使用连接运算符'+',str1+=str2;将被解释为 str1 = new StringBuilder().append(str1).append(str2).toString();

使用较少数量的字符串进行连接时,可以使用concat方法。但是,如果使用大量字符串,StringBuilder方法在性能方面会很快。

答案 6 :(得分:-2)

实际上,两者都是一样的。如果您看到concat(String paramString)的代码,它将返回一个新的字符串对象,并且在(+)运算符中它还将生成一个新的字符串对象。

如果您不想创建新对象,请使用字符串构建器连接两个字符串。

答案 7 :(得分:-2)

通常,将字符串与+concat()连接起来是一种不好的做法。如果要创建字符串,请改用StringBuilder