Java 字符串连接:concat() 与“+”运算符
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/47605/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
String concatenation: concat() vs "+" operator
提问by shsteimer
Assuming String a and b:
假设字符串 a 和 b:
a += b
a = a.concat(b)
Under the hood, are they the same thing?
在引擎盖下,它们是同一回事吗?
Here is concat decompiled as reference. I'd like to be able to decompile the +
operator as well to see what that does.
这里是 concat 反编译作为参考。我也希望能够反编译该+
运算符以查看其作用。
public String concat(String s) {
int i = s.length();
if (i == 0) {
return this;
}
else {
char ac[] = new char[count + i];
getChars(0, count, ac, 0);
s.getChars(0, i, ac, count);
return new String(0, count + i, ac);
}
}
采纳答案by Tom Hawtin - tackline
No, not quite.
不,不完全是。
Firstly, there's a slight difference in semantics. If a
is null
, then a.concat(b)
throws a NullPointerException
but a+=b
will treat the original value of a
as if it were null
. Furthermore, the concat()
method only accepts String
values while the +
operator will silently convert the argument to a String (using the toString()
method for objects). So the concat()
method is more strict in what it accepts.
首先,语义略有不同。如果a
是null
,则a.concat(b)
抛出 aNullPointerException
但a+=b
会将 的原始值a
视为null
。此外,该concat()
方法只接受String
值,而+
操作员会默默地将参数转换为字符串(使用toString()
对象的方法)。所以该concat()
方法在它接受的内容上更加严格。
To look under the hood, write a simple class with a += b;
要深入了解,请编写一个简单的类 a += b;
public class Concat {
String cat(String a, String b) {
a += b;
return a;
}
}
Now disassemble with javap -c
(included in the Sun JDK). You should see a listing including:
现在反汇编javap -c
(包含在 Sun JDK 中)。您应该会看到一个列表,其中包括:
java.lang.String cat(java.lang.String, java.lang.String);
Code:
0: new #2; //class java/lang/StringBuilder
3: dup
4: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_2
12: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokevirtual #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/ String;
18: astore_1
19: aload_1
20: areturn
So, a += b
is the equivalent of
所以,a += b
相当于
a = new StringBuilder()
.append(a)
.append(b)
.toString();
The concat
method should be faster. However, with more strings the StringBuilder
method wins, at least in terms of performance.
该concat
方法应该更快。但是,使用更多字符串时,该StringBuilder
方法会胜出,至少在性能方面是这样。
The source code of String
and StringBuilder
(and its package-private base class) is available in src.zip of the Sun JDK. You can see that you are building up a char array (resizing as necessary) and then throwing it away when you create the final String
. In practice memory allocation is surprisingly fast.
String
and StringBuilder
(及其包私有基类)的源代码可在 Sun JDK 的 src.zip 中找到。您可以看到您正在构建一个 char 数组(根据需要调整大小),然后在创建最终的String
. 在实践中,内存分配是惊人的快。
Update:As Pawel Adamski notes, performance has changed in more recent HotSpot. javac
still produces exactly the same code, but the bytecode compiler cheats. Simple testing entirely fails because the entire body of code is thrown away. Summing System.identityHashCode
(not String.hashCode
) shows the StringBuffer
code has a slight advantage. Subject to change when the next update is released, or if you use a different JVM. From @lukaseder, a list of HotSpot JVM intrinsics.
更新:正如 Pawel Adamski 所指出的,最近的 HotSpot 的性能发生了变化。javac
仍然产生完全相同的代码,但字节码编译器会作弊。简单的测试完全失败,因为整个代码体都被扔掉了。Summing System.identityHashCode
(not String.hashCode
) 显示StringBuffer
代码具有轻微优势。可能会在下一次更新发布时发生变化,或者如果您使用不同的 JVM。从@lukaseder,热点JVM内部函数列表。
回答by Niyaz
The + operatorcan work between a string and a string, char, integer, double or float data type value. It just converts the value to its string representation before concatenation.
的+运算可以在串和串,字符,整数,双精度或浮点数据类型值之间工作。它只是在连接之前将值转换为其字符串表示形式。
The concat operatorcan only be done on and with strings. It checks for data type compatibility and throws an error, if they don't match.
该毗连运算符只能与字符串来完成。它检查数据类型兼容性并在它们不匹配时抛出错误。
Except this, the code you provided does the same stuff.
除此之外,您提供的代码执行相同的操作。
回答by Bartosz Bierkowski
I don't think so.
我不这么认为。
a.concat(b)
is implemented in String and I think the implementation didn't change much since early java machines. The +
operation implementation depends on Java version and compiler. Currently +
is implemented using StringBuffer
to make the operation as fast as possible. Maybe in the future, this will change. In earlier versions of java +
operation on Strings was much slower as it produced intermediate results.
a.concat(b)
是在 String 中实现的,我认为自早期的 Java 机器以来,实现并没有太大变化。在+
操作实施取决于Java版本和编译器。目前+
正在实现使用,StringBuffer
以使操作尽可能快。也许在未来,这种情况会改变。在早期版本的 java 中,+
对字符串的操作要慢得多,因为它会产生中间结果。
I guess that +=
is implemented using +
and similarly optimized.
我想这+=
是使用+
和类似优化来实现的。
回答by Eli Courtwright
Niyazis correct, but it's also worth noting that the special + operator can be converted into something more efficient by the Java compiler. Java has a StringBuilder class which represents a non-thread-safe, mutable String. When performing a bunch of String concatenations, the Java compiler silently converts
Niyaz是正确的,但同样值得注意的是,Java 编译器可以将特殊的 + 运算符转换为更有效的东西。Java 有一个 StringBuilder 类,它表示一个非线程安全的可变字符串。当执行一堆字符串连接时,Java 编译器会默默地转换
String a = b + c + d;
into
进入
String a = new StringBuilder(b).append(c).append(d).toString();
which for large strings is significantly more efficient. As far as I know, this does not happen when you use the concat method.
对于大字符串,它的效率要高得多。据我所知,使用 concat 方法时不会发生这种情况。
However, the concat method is more efficient when concatenating an empty String onto an existing String. In this case, the JVM does not need to create a new String object and can simply return the existing one. See the concat documentationto confirm this.
但是,将空字符串连接到现有字符串时,concat 方法更有效。在这种情况下,JVM 不需要创建新的 String 对象,只需返回现有的对象即可。请参阅concat 文档以确认这一点。
So if you're super-concerned about efficiency then you should use the concat method when concatenating possibly-empty Strings, and use + otherwise. However, the performance difference should be negligible and you probably shouldn't ever worry about this.
因此,如果您非常关心效率,那么在连接可能为空的字符串时应该使用 concat 方法,否则使用 + 。但是,性能差异应该可以忽略不计,您可能永远不必担心这一点。
回答by Marcio Aguiar
How about some simple testing? Used the code below:
一些简单的测试怎么样?使用了下面的代码:
long start = System.currentTimeMillis();
String a = "a";
String b = "b";
for (int i = 0; i < 10000000; i++) { //ten million times
String c = a.concat(b);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
- The
"a + b"
version executed in 2500ms. - The
a.concat(b)
executed in 1200ms.
"a + b"
在2500 毫秒内执行的版本。- 将
a.concat(b)
在执行1200ms。
Tested several times. The concat()
version execution took half of the time on average.
测试了几次。该concat()
版本执行了对平均时间的一半。
This result surprised me because the concat()
method always creates a new string (it returns a "new String(result)
". It's well known that:
这个结果让我感到惊讶,因为该concat()
方法总是创建一个新字符串(它返回一个“ new String(result)
”。众所周知:
String a = new String("a") // more than 20 times slower than String a = "a"
Why wasn't the compiler capable of optimize the string creation in "a + b" code, knowing the it always resulted in the same string? It could avoid a new string creation. If you don't believe the statement above, test for your self.
为什么编译器不能优化“a + b”代码中的字符串创建,知道它总是导致相同的字符串?它可以避免创建新的字符串。如果你不相信上面的陈述,请测试你自己。
回答by Jason Cohen
Tom is correct in describing exactly what the + operator does. It creates a temporary StringBuilder
, appends the parts, and finishes with toString()
.
Tom 准确地描述了 + 运算符的作用是正确的。它创建一个临时的StringBuilder
,附加部分,并以toString()
.
However, all of the answers so far are ignoring the effects of HotSpot runtime optimizations. Specifically, these temporary operations are recognized as a common pattern and are replaced with more efficient machine code at run-time.
然而,到目前为止所有的答案都忽略了 HotSpot 运行时优化的影响。具体来说,这些临时操作被认为是一种常见的模式,并在运行时被更高效的机器代码所取代。
@marcio: You've created a micro-benchmark; with modern JVM's this is not a valid way to profile code.
@marcio:您已经创建了一个微基准;对于现代 JVM,这不是分析代码的有效方法。
The reason run-time optimization matters is that many of these differences in code -- even including object-creation -- are completely different once HotSpot gets going. The only way to know for sure is profiling your code in situ.
运行时优化很重要的原因是,一旦 HotSpot 开始运行,代码中的许多差异——甚至包括对象创建——就完全不同了。确定知道的唯一方法是在原位分析您的代码。
Finally, all of these methods are in fact incredibly fast. This might be a case of premature optimization. If you have code that concatenates strings a lot, the way to get maximum speed probably has nothing to do with which operators you choose and instead the algorithm you're using!
最后,所有这些方法实际上都非常快。这可能是过早优化的情况。如果您有大量连接字符串的代码,那么获得最大速度的方法可能与您选择的运算符无关,而是与您使用的算法无关!
回答by ckpwong
I ran a similar test as @marcio but with the following loop instead:
我运行了与@marcio 类似的测试,但使用以下循环:
String c = a;
for (long i = 0; i < 100000L; i++) {
c = c.concat(b); // make sure javac cannot skip the loop
// using c += b for the alternative
}
Just for good measure, I threw in StringBuilder.append()
as well. Each test was run 10 times, with 100k reps for each run. Here are the results:
为了更好的衡量,我也投了进去StringBuilder.append()
。每个测试运行 10 次,每次运行 100k 次。结果如下:
StringBuilder
wins hands down. The clock time result was 0 for most the runs, and the longest took 16ms.a += b
takes about 40000ms (40s) for each run.concat
only requires 10000ms (10s) per run.
StringBuilder
赢得胜利。大多数运行的时钟时间结果为 0,最长需要 16 毫秒。a += b
每次运行大约需要 40000 毫秒(40 秒)。concat
每次运行只需要 10000 毫秒(10 秒)。
I haven't decompiled the class to see the internals or run it through profiler yet, but I suspect a += b
spends much of the time creating new objects of StringBuilder
and then converting them back to String
.
我还没有反编译该类以查看内部结构或通过分析器运行它,但我怀疑a += b
花了很多时间创建 的新对象,StringBuilder
然后将它们转换回String
.
回答by Deepak Sharma
Basically, there are two important differences between + and the concat
method.
基本上,+ 和concat
方法之间有两个重要的区别。
If you are using the concatmethod then you would only be able to concatenate strings while in case of the +operator, you can also concatenate the string with any data type.
For Example:
String s = 10 + "Hello";
In this case, the output should be 10Hello.
String s = "I"; String s1 = s.concat("am").concat("good").concat("boy"); System.out.println(s1);
In the above case you have to provide two strings mandatory.
The second and main difference between +and concatis that:
Case 1:Suppose I concat the same strings with concatoperator in this way
String s="I"; String s1=s.concat("am").concat("good").concat("boy"); System.out.println(s1);
In this case total number of objects created in the pool are 7 like this:
I am good boy Iam Iamgood Iamgoodboy
Case 2:
Now I am going to concatinate the same strings via +operator
String s="I"+"am"+"good"+"boy"; System.out.println(s);
In the above case total number of objects created are only 5.
Actually when we concatinate the strings via +operator then it maintains a StringBuffer class to perform the same task as follows:-
StringBuffer sb = new StringBuffer("I"); sb.append("am"); sb.append("good"); sb.append("boy"); System.out.println(sb);
In this way it will create only five objects.
如果您使用的是concat方法,那么您将只能连接字符串,而在+运算符的情况下,您还可以连接具有任何数据类型的字符串。
例如:
String s = 10 + "Hello";
在这种情况下,输出应该是10Hello。
String s = "I"; String s1 = s.concat("am").concat("good").concat("boy"); System.out.println(s1);
在上述情况下,您必须提供两个字符串。
+和concat之间的第二个也是主要区别在于:
案例 1:假设我以这种方式使用concat运算符连接相同的字符串
String s="I"; String s1=s.concat("am").concat("good").concat("boy"); System.out.println(s1);
在这种情况下,池中创建的对象总数为 7,如下所示:
I am good boy Iam Iamgood Iamgoodboy
案例2:
现在我将通过+运算符连接相同的字符串
String s="I"+"am"+"good"+"boy"; System.out.println(s);
在上述情况下,创建的对象总数仅为 5。
实际上,当我们通过+运算符连接字符串时,它会维护一个 StringBuffer 类来执行以下相同的任务:-
StringBuffer sb = new StringBuffer("I"); sb.append("am"); sb.append("good"); sb.append("boy"); System.out.println(sb);
通过这种方式,它只会创建五个对象。
So guys these are the basic differences between +and the concatmethod. Enjoy :)
所以伙计们,这些是+和concat方法之间的基本区别。享受 :)
回答by iamreza
When using +, the speed decreases as the string's length increases, but when using concat, the speed is more stable, and the best option is using the StringBuilder class which has stable speed in order to do that.
使用 + 时,速度随着字符串长度的增加而降低,但使用 concat 时,速度更稳定,最好的选择是使用速度稳定的 StringBuilder 类来做到这一点。
I guess you can understand why. But the totally best way for creating long strings is using StringBuilder() and append(), either speed will be unacceptable.
我想你能理解为什么。但是创建长字符串的最佳方法是使用 StringBuilder() 和 append(),这两种速度都是不可接受的。
回答by dingalapadum
For the sake of completeness, I wanted to add that the definition of the '+' operator can be found in the JLS SE8 15.18.1:
为了完整起见,我想补充一点,“+”运算符的定义可以在JLS SE8 15.18.1 中找到:
If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.
The result of string concatenation is a reference to a String object that is the concatenation of the two operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in the newly created string.
The String object is newly created (§12.5) unless the expression is a constant expression (§15.28).
如果只有一个操作数表达式是字符串类型,则在运行时对另一个操作数执行字符串转换(第 5.1.11 节)以生成字符串。
字符串连接的结果是对 String 对象的引用,该对象是两个操作数字符串的连接。在新创建的字符串中,左侧操作数的字符位于右侧操作数的字符之前。
String 对象是新创建的(第 12.5 节),除非表达式是常量表达式(第 15.28 节)。
About the implementation the JLS says the following:
关于实施,JLS 说如下:
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive types, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间 String 对象。为了提高重复字符串连接的性能,Java 编译器可以使用 StringBuffer 类或类似的技术来减少通过评估表达式创建的中间 String 对象的数量。
对于原始类型,实现还可以通过直接从原始类型转换为字符串来优化包装对象的创建。
So judging from the 'a Java compiler may use the StringBuffer class or a similar technique to reduce', different compilers could produce different byte-code.
因此,从“Java 编译器可能使用 StringBuffer 类或类似技术来减少”来看,不同的编译器可能会产生不同的字节码。