gpt4 book ai didi

java - Java 如何使用 "+"进行字符串连接?

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:02:41 26 4
gpt4 key购买 nike

我阅读了有关 Java 使用 += 运算符的方式,使用 StringBuilder
它与 ("a"+ "b") 操作相同吗?

最佳答案

如果您组合文字 字符串(字面意思是 "foo"+ "bar"),编译器会在编译时执行,而不是在运行时执行。

如果你有两个非文字字符串并用 + 连接它们,编译器(无论如何是 Sun 的)将在幕后使用 StringBuilder,但不一定在最有效的方法。例如,如果你有这个:

String repeat(String a, int count) {
String rv;

if (count <= 0) {
return "";
}

rv = a;
while (--count > 0) {
rv += a;
}
return rv;
}

...Sun 编译器实际生成的字节码看起来像这样:

String repeat(String a, int count) {
String rv;

if (count <= 0) {
return "";
}

rv = a;
while (--count > 0) {
rv = new StringBuilder().append(rv).append(a).toString();
}
return rv;
}

(是的,真的——请参阅本答案末尾的反汇编。)请注意,它在每次迭代时都创建了一个新的 StringBuilder,然后将结果转换为 String。这是低效的(但这并不重要,除非你很多)因为所有的临时内存分配:它分配一个 StringBuilder 和它的缓冲区,很可能在第一个 append 上重新分配缓冲区 [如果 rv 的长度超过 16 个字符,这是默认的缓冲区大小],如果不是在第一个上,那么几乎可以肯定第二个 append,然后在末尾分配一个 String — 然后在下一次迭代时再次

如有必要,您可以通过重写它以显式使用 StringBuilder 来提高效率:

String repeat(String a, int count) {
StringBuilder rv;

if (count <= 0) {
return "";
}

rv = new StringBuilder(a.length() * count);
while (count-- > 0) {
rv.append(a);
}
return rv.toString();
}

我们在这里使用了显式的 StringBuilder 并将其初始缓冲区容量设置为足够大以保存结果。这更节省内存,但当然,对于没有经验的代码维护者来说不太清楚,而且编写起来也更痛苦。因此,如果您发现紧密字符串连接循环存在性能问题,这可能是一种解决方法。

您可以通过以下测试类看到这个隐藏的 StringBuilder:

public class SBTest
{
public static final void main(String[] params)
{
System.out.println(new SBTest().repeat("testing ", 4));
System.exit(0);
}

String repeat(String a, int count) {
String rv;

if (count <= 0) {
return "";
}

rv = a;
while (--count > 0) {
rv += a;
}
return rv;
}
}

...这样反汇编(使用 javap -c SBTest):

Compiled from "SBTest.java"
public class SBTest extends java.lang.Object{
public SBTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static final void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3; //class SBTest
6: dup
7: invokespecial #4; //Method "<init>":()V
10: ldc #5; //String testing
12: iconst_4
13: invokevirtual #6; //Method repeat:(Ljava/lang/String;I)Ljava/lang/String;
16: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
19: iconst_0
20: invokestatic #8; //Method java/lang/System.exit:(I)V
23: return

java.lang.String repeat(java.lang.String, int);
Code:
0: iload_2
1: ifgt 7
4: ldc #9; //String
6: areturn
7: aload_1
8: astore_3
9: iinc 2, -1
12: iload_2
13: ifle 38
16: new #10; //class java/lang/StringBuilder
19: dup
20: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V
23: aload_3
24: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: aload_1
28: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #13; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_3
35: goto 9
38: aload_3
39: areturn

}

请注意如何在循环的每次 迭代中创建新的 StringBuilder 并使用默认缓冲区容量创建。

所有这些临时分配的东西听起来很丑陋,但同样,只有当你处理大量的循环和/或大量的字符串时。此外,当生成的字节码运行时,JVM 可能会进一步优化它。例如,Sun 的 HotSpot JVM 是一个非常成熟的 JIT 优化编译器。一旦将循环识别为热点,它很可能会找到重构它的方法。或者不是,当然。 :-)

我的经验法则是,当我看到性能问题时,或者如果我知道我正在做很多连接并且很可能是一个性能问题,如果我改用 StringBuilder,从可维护性的角度来看,代码不会受到重大影响。在第二个问题上,狂热的反过早优化联盟可能不同意我的看法。 :-)

关于java - Java 如何使用 "+"进行字符串连接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2721998/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com