Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 字符串连接:concat()与“+”运算符_Java_String_Concatenation - Fatal编程技术网

Java 字符串连接:concat()与“+”运算符

Java 字符串连接:concat()与“+”运算符,java,string,concatenation,Java,String,Concatenation,假设字符串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];

假设字符串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);
    }
}
+运算符可以在字符串和字符串、字符、整数、双精度或浮点数据类型值之间工作。它只是在连接之前将值转换为其字符串表示形式

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

除此之外,您提供的代码也有相同的功能。

我不这么认为


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

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

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

String a = b + c + d;
进入

对于较大的字符串,这是非常有效的。据我所知,使用concat方法时不会发生这种情况

但是,将空字符串连接到现有字符串时,concat方法更有效。在这种情况下,JVM不需要创建新的字符串对象,只需返回现有的字符串对象即可。请参阅以确认这一点

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

不,不完全是

首先,在语义上有一点不同。如果a为null,那么a.concatb会抛出一个NullPointerException,但是a+=b会将a的原始值视为null。此外,concat方法只接受字符串值,而+运算符将使用对象的toString方法将参数静默地转换为字符串。所以concat方法在接受什么方面更严格

要了解情况,请编写一个a+=b的简单类

现在使用Sun JDK中包含的javap-c进行反汇编。您应该看到一个列表,其中包括:

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
concat方法应该更快。但是,字符串越多,StringBuilder方法就越有优势,至少在性能方面是如此

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


更新:正如Pawel Adamski所指出的,性能在最近的热点中发生了变化。javac仍然生成完全相同的代码,但字节码编译器会作弊。简单测试完全失败,因为整个代码体都被丢弃了。求和System.identityHashCode而不是String.hashCode表明StringBuffer代码有一点优势。在下一次更新发布时,或者如果您使用不同的JVM,则可能会发生更改。从,.

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

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代码中的字符串创建,因为它总是生成相同的字符串?它可以避免创建新的字符串。
如果你不相信上面的说法,那就自己测试一下

Tom准确地描述了+运算符的作用。它创建一个临时StringBuilder,附加零件,并以toString结束

然而,到目前为止,所有的答案都忽略了热点运行时优化的影响。具体来说,这些临时操作被认为是一种常见模式,并在运行时被更高效的机器代码所取代

@马西奥:你创造了一个;对于现代的JVM来说,这不是评测代码的有效方法

运行时优化之所以重要,是因为一旦HotSpot开始运行,代码中的许多差异(甚至包括对象创建)就完全不同了。唯一确定的方法是在原地分析代码


最后,所有这些方法实际上都非常快。这可能是过早优化的情况。如果您有大量串联字符串的代码,那么获得最大速度的方法可能与您选择的运算符无关,而与您使用的算法无关

我运行了一个与@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
}
只是为了好,m 当然,我也加入了StringBuilder.append。每个测试运行10次,每次运行10万次。结果如下:

StringBuilder轻松获胜。大多数运行的时钟时间结果为0,最长的运行时间为16毫秒。 a+=b每次运行大约需要40000毫秒40秒。 concat每次运行仅需要10000毫秒10秒。
我还没有对类进行反编译以查看其内部结构或通过探查器运行它,但我怀疑a+=b花费了大量时间创建StringBuilder的新对象,然后将它们转换回字符串。

基本上,+和concat方法之间有两个重要区别

如果使用concat方法,则只能连接字符串,而对于+运算符,还可以将字符串连接到任何数据类型

例如:

在这种情况下,输出应该是10Hello

在上述情况下,必须提供两个必填字符串

+和concat之间的第二个也是主要的区别是:

案例1: 假设我以这种方式使用concat运算符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:

现在,我将通过+运算符来表示相同的字符串

在上述情况下,创建的对象总数仅为5个

实际上,当我们通过+运算符连接字符串时,它会维护一个StringBuffer类来执行相同的任务,如下所示:-

StringBuffer sb = new StringBuffer("I");
sb.append("am");
sb.append("good");
sb.append("boy");
System.out.println(sb);
这样,它将只创建五个对象

这就是+和concat方法的基本区别。
享受:

使用+,速度会随着字符串长度的增加而降低,但使用concat时,速度会更稳定,最好的选择是使用具有稳定速度的StringBuilder类


我想你能理解为什么。但是,创建长字符串的最佳方法是使用StringBuilder和append,这两种方法的速度都是不可接受的。

为了完整起见,我想补充一点,“+”运算符的定义可以在以下内容中找到:

如果只有一个操作数表达式的类型为String,则为String 对另一个操作数执行§5.1.11转换,以生成 运行时的字符串

字符串连接的结果是对字符串对象的引用 这是两个操作数字符串的串联。人物 左操作数的字符位于右操作数的字符之前 新创建的字符串中的操作数

字符串对象是新创建的§12.5,除非表达式是 常量表达式§15.28

关于实施,JLS说明如下:

实现可以选择执行转换和串联 在一个步骤中避免创建然后丢弃中间 字符串对象。以提高重复字符串的性能 连接时,Java编译器可以使用StringBuffer类或 减少中间字符串对象数量的类似技术 通过表达式求值创建的

对于基元类型,实现还可以优化 通过直接从基元转换来创建包装器对象 键入一个字符串


因此,从“Java编译器可能使用StringBuffer类或类似技术来减少”判断,不同的编译器可能产生不同的字节码

这里的大多数答案都是2008年的。随着时间的推移,情况似乎发生了变化。我用JMH制作的最新基准测试表明,在Java8+上的速度大约是concat的两倍

我的基准:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}
结果:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s

concat事实上并没有做到这一点。我已经用concat方法的反编译编辑了我的文章,实际上它是这样做的。看一看concat代码的第一行。concat的问题在于它总是生成一个新的String@MarcioAguiar:也许你的意思是+总是生成一个新字符串-正如你所说,当你输入一个空字符串时,concat有一个例外。当前+是使用StringBuffer False实现的,它是StringBuilder。StringBuffer是StringBuilder的threadsafe impl。它在java 1.5之前是StringBuffer,这是StringBuffer首次引入时的版本。我猜这些临时操作的意思是使用转义分析在堆栈上分配堆对象,只要可以证明是正确的。虽然escape分析在HotSpot中出现,可用于删除某些同步,但我不相信,在撰写本文时,我不确定+的可能副本可以反编译。使用javap反汇编Java类文件。由于“不可变”,您可能应该使用StringBuffer或StringBuilder线程,因此速度更快,相反,对象创建时间真的很重要。这就是为什么在许多情况下,我们直接使用StringBuilder,而不是利用+后面的StringBuilder的原因。@coolcfan:当+用于两个字符串时,有没有比使用StringBuilder更好的情况
n将是String.valueOfs1.concats2?你知道为什么编译器不使用后者[或者在已知s1为非null的情况下省略valueOf call]?@supercat抱歉,我不知道。也许支持这种糖的人是最好的答案。搜索:invokedynamicStringConcatFactory@HyperLink您可以在使用javap-c的编译类上看到该代码。哦,答案是这样的。您只需要解释字节码反汇编,这应该没有那么困难。您可以查阅以了解各个字节码。你想参考的内容在第六章。有点晦涩,但你可以很容易地理解它的要点。我想知道为什么Java编译器在连接两个字符串时也使用StringBuilder?如果字符串包含连接最多四个字符串的静态方法,或字符串[]中的所有字符串,则代码可以通过两个对象分配追加最多四个字符串-结果字符串及其备份字符[],没有一个冗余,任何数量的字符串都有三个分配-字符串[]、结果字符串和备份字符[],只有第一个是多余的。实际上,使用StringBuilder最多需要四次分配,并且需要将每个字符复制两次。这不是意味着:a=a+b吗?自从这个答案创建以来,情况已经发生了变化。请阅读我下面的回答。亲爱的,您很清楚,任何字符串文字都被视为字符串对象本身,存储在字符串池中。因此,在本例中,我们有4个字符串文字。因此,显然,至少应该在字符串池中创建4个对象。我不这么认为:字符串s=I+am+good+boy;字符串s2=go.concatod;System.out.printlns2==s2.intern;打印true,这意味着good在打电话给interne之前不在字符串池中。我只说这行字符串s=I+am+good+boy;在这种情况下,所有4个是字符串文字都保存在一个池中。因此,应在池中创建4个对象。使用+运算符相当于使用StringBuilder@ihebiheb或StringBuffer@ihebiheb字符串连接运算符的实现由Java编译器自行决定,只要编译器最终符合Java™ 语言规范。例如,javac编译器可能使用StringBuffer、StringBuilder或java.lang.invoke.StringConcatFactory实现运算符,具体取决于JDK版本。。。。我想知道为什么Java字符串从未包含通过连接字符串[]的元素来形成字符串的静态函数。使用+连接8个字符串使用这样的函数将需要构造并在以后放弃字符串[8],但这将是唯一需要构造并放弃的对象,而使用StringBuilder将需要构造并放弃StringBuilder实例和至少一个char[]backing store.@supercat在Java 8中添加了一些静态String.join方法,作为Java.util.StringJoiner类的快速语法包装器。@TiStrga:对+的处理是否更改为使用此类函数?@supercat会破坏二进制向后兼容性,所以没有。这只是为了回答为什么字符串从未包含静态函数注释:现在有这样一个函数。遗憾的是,您的提案的其余部分“重构+”使用它所需要的远远超过Java开发人员愿意更改的内容。@TiStrga:Java字节码文件是否有任何方法可以指示函数X是否可用,请调用它;否则,是否以加载类过程中可以解决的方式执行其他操作?使用静态方法生成代码,该方法既可以链接到Java的静态方法,也可以在不可用的情况下使用stringbuilder。我在Java jdk1.8.0241上测试了您的代码,对我来说,a+b代码提供了优化的结果。含浓度:203ms,含+:113ms。我猜在以前的版本中,它并没有优化。
String s = "I";
String s1 = s.concat("am").concat("good").concat("boy");
System.out.println(s1);
String s="I";
String s1=s.concat("am").concat("good").concat("boy");
System.out.println(s1);
I
am
good
boy
Iam
Iamgood
Iamgoodboy
String s="I"+"am"+"good"+"boy";
System.out.println(s);
StringBuffer sb = new StringBuffer("I");
sb.append("am");
sb.append("good");
sb.append("boy");
System.out.println(sb);
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}
Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s