将整数连接到字符串 - 从性能和内存角度来看,使用字符串字面量还是基元类型?

10

选项1:

String newStr = someStr + 3 + "]";

选项2:

String newStr = someStr + "3" + "]";

在性能、内存和一般实践方面,哪个选项更好?有哪些推荐的工具/方法可以用来测量代码的内存使用情况和性能(除了测量开始时间和结束时间并计算差异之外)?


两者都没有任何区别。 - Smit
@Smit:编译器将使用 StringBuilder 进行字符串连接。 - jlordo
@Smit:使用 StringBuilder 会导致与问题中的代码相同或更糟的字节码。 - JB Nizet
@JBNizet 和 @jlordo,但是这个SO答案说了不同的事情。我理解错了吗?Java如何使用“+”进行字符串连接? - Smit
阅读回答的结尾和评论。该答案一开始是错误的,然后试图变得正确。阅读Mikhail Vladimirov对这个问题的回答。 - JB Nizet
5个回答

11

第一句话将变成:

StringBuilder sb = new StringBuilder (String.valueOf (someStr));
sb.append (3);
sb.append ("]");
String newStr = sb.toString ();

第二个将变成:

StringBuilder sb = new StringBuilder (String.valueOf (someStr));
sb.append ("3");
sb.append ("]");
String newStr = sb.toString ();

这里是分解:

public String foo (String someStr)
{
    String newStr = someStr + 3 + "]";
    return newStr;
}

public String bar (String someStr)
{
    String newStr = someStr + "3" + "]";
    return newStr;
}

public java.lang.String foo(java.lang.String);
Code:
   0: new           #16                 // class java/lang/StringBuilder
   3: dup
   4: aload_1
   5: invokestatic  #18                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   8: invokespecial #24                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  11: iconst_3
  12: invokevirtual #27                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  15: ldc           #31                 // String ]
  17: invokevirtual #33                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  20: invokevirtual #36                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  23: astore_2
  24: aload_2
  25: areturn

public java.lang.String bar(java.lang.String);
Code:
   0: new           #16                 // class java/lang/StringBuilder
   3: dup
   4: aload_1
   5: invokestatic  #18                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   8: invokespecial #24                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  11: ldc           #44                 // String 3
  13: invokevirtual #33                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  16: ldc           #31                 // String ]
  18: invokevirtual #33                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  21: invokevirtual #36                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  24: astore_2
  25: aload_2
  26: areturn

3
第一个会添加3(装箱为整数3),然后添加]。第二个会添加3] - Steve Kuo
我不知道为什么,但我的眼睛在第二种情况下看到了常量,而在第一种情况下没有看到。 - JB Nizet
糟糕。Steve Kuo是对的。String s = "hello" + 3 + "]"会创建一个单一的字符串"hello3]"。奇怪的是编译器不能优化这种情况。还是要点赞,因为我学到了东西。 - JB Nizet
@JB Nizet:编译器只需优化常量表达式。方法参数不是常量。 - meriton

7

两者之间不会有任何明显的区别。使用你认为最合乎逻辑和易读的方式。我会使用

String newStr = someStr + "3]";

3
我建议使用Jprofiler作为一款优秀的Java应用程序性能分析工具,它帮助我找到了很多内存问题。
我认为选项1和选项2在内存使用方面没有太大差异,尤其是对于桌面应用程序而言。

2
假设someString是常量,两者都是“常量表达式”,将在编译时进行评估。它们将产生相同的类文件和运行时行为。
来源:Java语言规范写道:
引用: 编译时常量表达式是指表示原始类型或String值的表达式,不会突然完成并且仅使用以下内容组成:
  • 原始类型的文字和String类型的文字(§3.10.1、§3.10.2、§3.10.3、§3.10.4、§3.10.5)
  • 加法操作符+和减法操作符-(§15.18)
  • ...
String类型的编译时常量表达式总是被“interned”,以共享唯一实例,使用方法String.intern。
如果someString不是一个常量,则大多数现代编译器将使用StringBuilder,该类在Java语言规范中明确允许
引用如下:
字符串连接的结果是一个字符串对象的引用,该对象是两个操作数字符串的连接。新创建的字符串中,左操作数的字符位于右操作数的字符之前。
除非表达式是编译时常量表达式(§15.28),否则将新创建String对象(§12.5)。
为了避免创建和丢弃中间的String对象,实现可能会选择一步完成转换和连接。为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似的技术来减少通过评估表达式创建的中间String对象的数量。
对于原始类型,实现也可以通过直接从原始类型转换为字符串来优化掉包装对象的创建。

0
每当您连接字符串时,每次连接都会创建字符串的新副本,并且两个字符串都会逐个字符复制。这导致时间复杂度为O(n²)(McDowell)。
如果您想提高性能,请使用
StringBuilder

其中一个构造函数的语法如下:
public StringBuilder(int size); //Contains no character. Initial capacity of 'size'. 

StringBuilder(可变字符序列。请记住,字符串是不可变的)通过简单地创建所有字符串的可调整大小的数组来帮助解决此问题。只有在必要时才将它们复制回字符串(麦克道威尔)。

StringBuilder str = new StringBuilder(0);
str.append(someStr);
str.append(3);
str.append("]");

参考资料:

麦克道威尔,盖尔·拉克曼。《破解编程面试》,第6版。印刷版。

“Stringbuilder (Java Platform SE 8 )”。Docs.oracle.com。N.p.,2016年。网络。2016年6月4日。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接