连接运算符 (+) 与 concat() [重复]
Posted
技术标签:
【中文标题】连接运算符 (+) 与 concat() [重复]【英文标题】:Concatenation operator (+) vs. concat() [duplicate] 【发布时间】:2012-02-04 00:27:09 【问题描述】:对于字符串连接,我们可以使用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()
比运算符快,那么我们什么时候应该使用串联运算符(+)
?
【问题讨论】:
阅读:***.com/questions/693597/… 和 ***.com/questions/47605/java-string-concatenation 关于唯一看起来不同的是,当您添加的字符串长度为零时,它只会返回原始字符串,而不是创建新字符串。 + 运算符可能有点贵……如果您正在执行数百或数千个字符串构建操作,请查看 StringBuffer.append()。通常会看到一个方法建立一个 StringBuffer,然后在最后返回或使用 theBuffer.toString()。 @PankajKumar 好吧,你可能想要 StringBuilder 而不是 StringBuffer 【参考方案1】:concat 方法总是产生一个带有连接结果的新字符串。
加号运算符由创建 StringBuilder 支持,附加您需要的所有 String 值并进一步调用它的 toString()。
因此,如果您需要连接两个值,concat() 将是更好的选择。如果需要连接 100 个值,则应使用加号运算符或显式使用 StringBuilder(例如,在循环中追加的情况下)。
【讨论】:
您后面的评论仅适用于 同一表达式 中的所有字符串连接。所以一个StringBuilder
用于x = a + b + c
(假设都是String
s),但两个StringBuilder
s用于x = a + b; x += c;
。
作为 Artem 答案的附录,this 链接,这似乎是 09 年关于这个特定问题的博客文章。希望该链接将为 Artem 和 TJ 提供的答案和 cmets 提供更多背景信息,尽管我无法验证文章中的断言,因为我承认自己不是 Java 专家...
你不是说explicitly use StringBuilder
谢谢你,我的英语还很差:-)
"Concat 方法总是产生一个带有连接结果的新字符串。"不,它没有:if (otherLen == 0) return this;
【参考方案2】:
其实两者都是一样的。如果你看到concat(String paramString)
的代码,它会返回一个新的字符串对象,并且在(+)运算符中它还会生成一个新的字符串对象。
如果您不想创建新对象,请使用字符串生成器连接两个字符串。
【讨论】:
至于 (+) 大部分都不是真的【参考方案3】:一般来说,将字符串与+
和concat()
连接是一种不好的做法。如果要创建字符串,请改用StringBuilder
。
【讨论】:
这显然是一个错误的答案。在很多情况下,我们应该使用 + 或 concat() 来代替 StringBuilder/StringBuffer。 问题中显示的情况是在循环中串联字符串,在这种情况下,我相信 StringBuilder 是最好的方法,不仅在 Java 中。【参考方案4】:我相信连接的“风格”会有所作为。
对于 concat(),它在内部创建一个新的 char 数组缓冲区,并根据该 char 数组返回一个新字符串。
对于 + 运算符,编译器实际上将其转换为使用 StringBuffer/StringBuilder。
因此,如果要连接两个字符串,concat() 绝对是更好的选择,因为创建的对象数量只是结果字符串(以及内部使用的 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();
【讨论】:
盈亏平衡点大约是四个字符串;(String.valueOf(strA).concat(strB)).concat(String.valueOf(strC).concat(strD))
与使用字符串生成器的相对效率大致是一个折腾。对于最多八个字符串,连接可能会更慢,但绝不会如此,并且在重要的情况下可能会更快(它最终会将每个字符精确复制三次;StringBuilder
将复制每个字符两次,如果它永远不必扩展,但如果字符串很大,则可能需要扩展)。【参考方案5】:
您可以始终使用 +
,前提是您只使用 >= Java 1.5 并且您不在环形。在 Java 1.5 中,它会导致创建 new StringBuilder
并对其进行处理,直到您的字符串完成为止。这是最快的方法。
无论如何 - 如果你在一个循环中(并用+
连接字符串) - 循环的每次迭代都会创建一个new StringBuilder
- 这不是最好的主意。所以这是你应该强制使用StringBuilder
或StringBuffer
(线程安全)类的地方。
通常,此链接清楚地回答了您的问题,并为您提供了完整的知识:
http://littletutorials.com/2008/07/16/stringbuffer-vs-stringbuilder-performance-comparison/
【讨论】:
【参考方案6】:虽然操作符和方法都给出相同的输出,但它们内部的工作方式不同。
concat()
方法只连接 str1 和 str2 并输出一个字符串,对于少量连接更有效。
但使用连接运算符 '+', str1+=str2
;将被解释为
str1 = new StringBuilder().append(str1).append(str2).toString();
当使用较少数量的字符串进行连接时,您可以使用 concat 方法。但是,如果您使用大量字符串,则 StringBuilder 方法在性能方面会很快。
【讨论】:
构造一个字符串构造器并重复使用它对性能有好处,但是创建一个字符串构造器,使用它连接少于四个字符串,然后放弃它会适得其反。不幸的是,编译器在使用它们连接两个字符串后经常使用并放弃字符串构建器。【参考方案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()
似乎使用更多。
【讨论】:
“但是它花费了你很多,远远超过你的时间(这可能更值得)”这里的成本意味着打字和概念开销?但是,如果我们忽略 15 毫秒的好处,可以说内存使用量是 1/10 @Naroji 我看不出内存是 1/10 的多少,15ms 是按一个键所需的时间,在开发时间方面确实微不足道。 消耗的内存是 1/10,正如问题中所提到的。连接运算符:耗时 =31 内存消耗 =“2,259,096” 连接方法:耗时 =16 内存消耗 =“299,592” 60 Hz (~16.66 ms) 滴答计数器?【参考方案8】:其实s1 + s2
和s1.concat(s2)
是很不一样的。
s1 + s2
被javac转换成
(new StringBuilder(String.valueOf(s1)).append(s2).toString();
反编译.class可以看到。这种结构不是很有效;它涉及最多三个new char[]
分配和三个char[]
复制操作。
s1.concat(s2)
永远是一个new 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);
注意new String(int, int, char[])
是String
的包私有构造函数。它直接使用char buf[]
,无需通常的复制,以确保String
不变性的buf
不可见性。
【讨论】:
我想知道为什么 javac 使用StringBuilder
,即使在组合少于四个字符串时也是如此?我认为String.valueOf(strA).concat(strB).concat(strC)
会生成更紧凑的代码,并且——对于字符串长度的每种组合——比strA+strB+strC
实际生成的代码更快,除非 JITter 用更有效的东西替换调用StringBuilder
的代码。跨度>
以上是关于连接运算符 (+) 与 concat() [重复]的主要内容,如果未能解决你的问题,请参考以下文章
MySQL GROUP_CONCAT 返回重复值。无法使用 DISTINCT