将字符串作为变量与字符连接与与其他字符串连接有什么区别?

What is the difference when concatenating a String as a variable with a Character vs concatenating with an other String?

当我看到这样的东西(伪 1-liner)时:

str1 + "a" + str2

是不是比下面的(伪1-liner)差很多(或者better/equal)?

str1 + 'a' + str2

更新: 更好的示例(来自@QPaysTaxes)以减少对我的原始示例的混淆。

我尝试了什么: 过去 10 年编程的各种东西 Java 但我从来没有真正看到引擎盖下的东西 - 例如我会 假设 第二个稍微 "faster/better" 因为没有为 slash-sign and/or 垃圾收集器创建 String-Object(s) Java 的人需要处理的更少。 我曾经为 Java 证书做准备并且在那个时候可能会更好地争论,但它似乎是我的日常业务 "theory" 关于 Java 必须保持日期也是……我知道没有比我的假设更好的解释了应该使用 indexOf('c') 而不是 indexOf("C") 我想知道 String-concatenation 是否同样重要。

我也用谷歌搜索了一下,但正如我的标题可能暗示的那样,如果没有示例,我不太好描述我正在寻找的东西。我对此感到抱歉,而且这个障碍可能会产生重复。

我会尝试什么: 基于此处接受的答案 String concatenation: concat() vs "+" operator 我希望能够开始了解引擎盖下的内容,并且有一天能够争论/回答这些深刻的问题。

如果您构造了一个文件名,您肯定会在以后使用它。在大多数情况下,这涉及访问物理媒体,这比连接字符串时可能做错的任何事情都慢得多。所以,做可维护的事情,不要担心这种特殊情况下的性能。

我在构建文件名时的建议是使用 File class or Path,它会自动确保路径分隔符正确。

编辑:正如您在评论中指出的那样,您的问题是关于一般情况的。只要看看来源。 StringBuilder.append(String) 最终在 String.getChars() 中执行了 System.arraycopy()StringBuilder.append(char) 直接复制了一个字符。所以理论上,StringBuilder.append(char)会更快。

但是,您必须对此进行基准测试,看看它在实践中是否有任何不同。

我不确定这两个选项在性能方面是否更好,但我可以想到另一个要考虑的问题,那就是第一个片段。

如果您附加基元而不是这些基元的 String 表示形式,编译器可以更好地防止打字错误。

考虑:

String plus10 = "plus" + 10;

如果打错了

String plus10 =  "plus" + 1O;

编译器会报错。

另一方面,如果您键入

String plus10 =  "plus" + "1O";

编译器对此没有问题。

同样适用于追加chars

String plus = "x" + '++' + "y";

不会编译,而

String plus = "x" + "++" +  "y";

将通过编译。

当然最好使用常量而不是硬编码值(并附加到 StringBuilder 而不是使用 String 连接),但即使是常量我也更喜欢原始的类型优于字符串,因为它们为您提供了更高级别的错误保护。

实际上在性能上没有任何显着差异。平均需要相同的时间来进行字符串连接。

但是,内部 Java 编译器在编译时将 + 运算符替换为 StringBuilder

所以当对char使用+运算符时,编译器会在内部将其转换为StringBuilder并使用.append(char)。字符串也会发生同样的情况,不同之处在于它将使用 .append(String)

正如我上面提到的,平均没有差异。简单的测试会显示时间差接近于0。所以这真的是可读性问题。而且从可读性的角度来看,如果你是集中字符串,最好保持类型相同,即使是单个字符也使用 String,而不是 char

查看源代码通常有助于理解发生了什么。

String s = s1 + s2

将执行:

String s = new StringBuilder(s1).append(s2).toString();

现在查看 class StringBuilder 的 append(char) 和 append(string) 的源代码:

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28char%29

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/AbstractStringBuilder.java#AbstractStringBuilder.append%28java.lang.String%29

您将看到 append(string) 执行更多检查以查看字符串是否为 null 或空。但是,您可能不会注意到任何差异。

这是引擎盖下的内容:String str = s1 + "/"; 实质上创建了 2 个新的独立字符串对象(strnew String("/"))

这对于小型软件来说没有问题,但是如果您要创建 2 个 String 对象(请记住:对象在 Stack 中保留 1 个条目加上在 Heap 中保留的内容),请考虑内存方面的问题 n > 500.000 个数据库条目。

使用单引号,如 String str = s1 + '/',将导致完全不同的过程。 '/' 代表在引号之间写入的任何 单个 字符的数字 ASCii 字符表示值。此操作具有常量 (O(1)) 运行时(想想即时数组访问)并且自然会比创建和引用对象更快。

正如很多人已经建议的那样,使用 StringBuilder 对象进行字符串连接比使用 + 运算符构建字符串更容易记忆。

Based on the accepted answer here I hope to be able to have a start to see whats under the hood.

让我们看一下将字符串与字符连接时生成的字节码:

String str1 = "a" + "test";
String str2 = 'a' + "test";

0: ldc           #2                  // String atest
2: astore_1
3: ldc           #2                  // String atest
5: astore_2

如你所见,没有区别,编译器会将其转换为相同的字节码。

现在让我们看一下将字符连接到字符串变量时生成的字节码。

String str1 = "a" + str3; //str3 is a String
String str2 = 'a' + str3; 

 7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
10: ldc           #5                  // String a
12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: new           #3                  // class java/lang/StringBuilder
26: dup
27: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
30: bipush        97
32: invokevirtual #8                  // Method java/lang/StringBuilder.append:(C)Ljava/lang/StringBuilder;
35: aload_1
36: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

如您所见,有一点不同。

10: ldc           #5                  // String a

ldc 将常量池(String、int 或 float)中的常量 #index 压入堆栈。

因此,如果您直接与一个变量连接,连接一个字符将生成更少的字节码,这就是幕后

现在对于性能问题,这不会代表任何显着的性能差异,因为 JIT 编译器优化了 大多数 的临时对象,除非您在 运行 您的程序中指定使用 -Djava.compiler=NONE.

禁用 JIT 编译器

我更喜欢使用 "a" 而不是 'a' 来确保结果是 String

考虑一下:

public static void main(String... args) {
    String s = "foo";
    int i = 1;
    Object arg = s + '/' + i;
    log(arg);
}

private static void log(Object... args) {
    MessageFormat format = new MessageFormat("bar {0}");
    String message = format.format(args);
    System.out.println(message); // or write to a log or something
}

假设您决定不再需要消息中的 s 并将 main 方法中的第三行更改为:

Object arg = '/' + i;

然后 arg 将只包含一个数字,因为 char + int 不连接,而是添加值。