【问题标题】:Concat an integer to a String - use String literal or primitive from performance and memory point of view?将整数连接到字符串 - 从性能和内存的角度来看,使用字符串文字还是原语?
【发布时间】:2013-01-24 19:45:08
【问题描述】:

选项 1:

String newStr = someStr + 3 + "]";

选项 2:

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

就性能、内存和一般做法而言,哪个选项更好? 我可以使用哪些推荐的工具/方法来测量我的代码的内存使用情况及其性能(除了测量开始时间和结束时间并计算差异)

【问题讨论】:

  • 两者都没有任何区别。
  • @Smit:编译器将使用StringBuilder 进行字符串连接
  • @Smit:使用 StringBuilder 会导致与问题中的代码相同的字节码,或者更糟。
  • @JBNizet 和 @jlordo 但这个 SO 答案说的不同。我理解错了什么? How Java do the string concatenation using “+”?
  • 阅读答案的结尾和 cmets。答案是先弄错了,然后再试着改正。阅读 Mikhail Vladimirov 对这个问题的回答。

标签: java string concatenation primitive


【解决方案1】:

两者之间不会有任何明显的区别。使用您认为最符合逻辑和可读性的内容。我会用

String newStr = someStr + "3]";

【讨论】:

    【解决方案2】:

    我会推荐 Jprofiler 作为一个很棒的 Java 应用程序分析工具,它可以帮助我发现很多内存问题。

    我不认为选项 1 和 2 在内存使用方面有很大差异,尤其是对于桌面应用程序。

    【讨论】:

      【解决方案3】:

      第一个会变成:

      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]
      • +1。我不知道为什么,但我的眼睛在第二种情况下看到了常数,但在第一种情况下没有。
      • 糟糕。史蒂夫郭是对的。 String s = "hello" + 3 + "]" 虽然创建了一个字符串 "hello3]"。奇怪的是编译器无法优化这种情况。 +1 仍然是因为我学到了一些东西。
      • @JB Nizet:编译器只需要针对常量表达式进行优化。方法参数不是常量。
      【解决方案4】:

      假设 someString 是常量,那么两者都是常量表达式,并且将在编译时进行计算。它们将产生相同的类文件和运行时行为。

      来源:Java Language Specification 写道:

      编译时常量表达式是表示原始类型值或字符串的表达式,它不会突然完成并且仅使用以下内容组成:

      • 原始类型的文字和字符串类型的文字(§3.10.1、§3.10.2、§3.10.3、§3.10.4、§3.10.5)

      • 加法运算符 +- (§15.18)

      • ...

      使用 String.intern 方法,String 类型的编译时常量表达式始终是“内部”的,以便共享唯一的实例。

      如果 someString 不是常量,大多数现代编译器将使用 StringBuilder,它被 Java 语言规范明确指定为 permitted

      字符串连接的结果是对一个字符串对象的引用,该对象是两个操作数字符串的连接。在新创建的字符串中,左侧操作数的字符在右侧操作数的字符之前。

      String 对象是新创建的(第 12.5 节),除非表达式是编译时常量表达式(第 15.28 节)。

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

      对于原始类型,实现还可以通过直接从原始类型转换为字符串来优化包装对象的创建。

      【讨论】:

        【解决方案5】:

        每当您连接一个字符串时,在每次连接时,您都会创建该字符串的一个新副本,并且两个字符串都会被复制,一次一个字符。这导致时间复杂度为 O(n2) (McDowell)。

        如果你想提高性能,使用

        StringBuilder
        

        其中一个构造函数具有以下语法:

        public StringBuilder(int size); //Contains no character. Initial capacity of 'size'. 
        

        StringBuilder(可变的字符序列。记住字符串是不可变的)通过简单地创建一个包含所有字符串的可调整大小的数组来帮助解决这个问题。仅在必要时将它们复制回字符串 (McDowell)。

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

        参考:

        麦克道威尔,盖尔·拉克曼。破解编码访谈,第 6 版。打印。

        “字符串生成器(Java 平台 SE 8)”。 Docs.oracle.com。 N.p.,2016 年。网络。 2016 年 6 月 4 日。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多