假设字符串a和b:

a += b
a = a.concat(b)

在引擎盖下,它们是一样的吗?

这里是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);
    }
}

当前回答

做一些简单的测试怎么样?使用下面的代码:

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);

“a + b”版本在2500ms内执行。 a.concat(b)在1200ms内执行。

测试了几次。concat()版本的执行平均花费了一半的时间。

这个结果让我很惊讶,因为concat()方法总是创建一个新字符串(它返回一个“new string (result)”)。众所周知:

String a = new String("a") // more than 20 times slower than String a = "a"

为什么编译器不能在“a + b”代码中优化字符串创建,知道它总是导致相同的字符串?它可以避免创建新的字符串。 如果你不相信上面的说法,自己测试一下。

其他回答

+运算符可以在字符串、char、integer、double或float数据类型值之间工作。它只是在连接之前将值转换为字符串表示形式。

concat操作符只能在字符串上执行。它检查数据类型兼容性,如果不匹配,则抛出错误。

除此之外,您提供的代码也做同样的事情。

我不这么想。

a.concat(b)是在String中实现的,我认为自早期java机器以来,实现没有太大变化。+操作的实现取决于Java版本和编译器。目前+是使用StringBuffer实现的,以使操作尽可能快。也许在未来,这种情况会改变。在java +的早期版本中,对字符串的操作要慢得多,因为它产生中间结果。

我猜+=是使用+实现的,并进行了类似的优化。

Niyaz是正确的,但同样值得注意的是,特殊的+运算符可以被Java编译器转换为更有效的东西。Java有一个StringBuilder类,它表示一个非线程安全的可变String。当执行一系列String连接时,Java编译器会静默地进行转换

String a = b + c + d;

into

String a = new StringBuilder(b).append(c).append(d).toString();

这对于大字符串来说效率更高。据我所知,使用concat方法时不会发生这种情况。

然而,concat方法在将空字符串连接到现有字符串时效率更高。在这种情况下,JVM不需要创建新的String对象,只需返回现有的String对象。请参阅concat文档来确认这一点。

所以如果你非常关心效率,那么你应该在连接可能为空的字符串时使用concat方法,否则使用+。然而,性能差异应该可以忽略不计,您可能永远都不应该担心这一点。

不,不完全是。

首先,语义上略有不同。如果a为空,则a.c concat(b)抛出NullPointerException,但a+=b将把a的原始值视为空值。此外,concat()方法只接受String值,而+运算符将无声地将参数转换为String(对对象使用toString()方法)。因此concat()方法在接受内容方面更加严格。

为了深入了解,用a += b写一个简单的类;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

现在使用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

a += b等于

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

concat方法应该更快。但是,对于更多的字符串,StringBuilder方法胜出,至少在性能方面是这样。

String和StringBuilder的源代码(以及它的包私有基类)可以在Sun JDK的src.zip中找到。您可以看到您正在构建一个char数组(根据需要调整大小),然后在创建最终String时将其丢弃。实际上,内存分配的速度快得惊人。

更新:正如Pawel Adamski所指出的,性能在最近的HotSpot中有所改变。Javac仍然生成完全相同的代码,但是字节码编译器会作弊。简单的测试完全失败,因为整个代码体都被丢弃了。总结系统。identityHashCode(不是String.hashCode)显示StringBuffer代码有一点优势。可能会在发布下一次更新时更改,或者如果您使用不同的JVM。来自@lukaseder的HotSpot JVM intrinsic列表。

做一些简单的测试怎么样?使用下面的代码:

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);

“a + b”版本在2500ms内执行。 a.concat(b)在1200ms内执行。

测试了几次。concat()版本的执行平均花费了一半的时间。

这个结果让我很惊讶,因为concat()方法总是创建一个新字符串(它返回一个“new string (result)”)。众所周知:

String a = new String("a") // more than 20 times slower than String a = "a"

为什么编译器不能在“a + b”代码中优化字符串创建,知道它总是导致相同的字符串?它可以避免创建新的字符串。 如果你不相信上面的说法,自己测试一下。