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

对于字符串连接,我们可以使用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()比运算符更快,那么我们何时应该使用连接运算符(+)

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

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

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

事实上,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秒,每个循环在一个单独的方法中才有意义。 短期测试可能难以重现和比较。 从您的时间看,您似乎正在使用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运行时打印-XX:-UseTLAB -mx1g

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

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

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

对于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(); 

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

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

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

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

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

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

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

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

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

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

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