简体   繁体   English

连接多个字符串时会创建多少个String对象?

[英]How many String objects would be created when concatenating multiple Strings?

I was asked in an interview about the number of objects that will be created on the given problem: 我在接受采访时被问到将在给定问题上创建的对象数量:

String str1 = "First";
String str2 = "Second";
String str3 = "Third";
String str4 = str1 + str2 + str3;

I answered that there would be 6 objects created in the string pool. 我回答说在字符串池中会创建6个对象

3 would be for each of the three variables. 3将是三个变量中的每一个。
1 would be for str1 + str2 (let's say str ). 1将是str1 + str2 (让我们说str )。
1 would be for str2 + str3 . 1将用于str2 + str3
1 would be for the str + str3 ( str = str1 + str2 ). 1将用于str + str3str = str1 + str2 )。

Is the answer I gave correct? 我给出的答案是否正确? If not, what is the correct answer? 如果没有,那么正确的答案是什么?

Any answer to your question will depend on the JVM implementation and the Java version currently being used. 您的问题的任何答案将取决于JVM实现和当前使用的Java版本。 I think it's an unreasonable question to ask in an interview. 我认为在面试中提出这是一个不合理的问题。

Java 8 Java 8

On my machine, with Java 1.8.0_201, your snippet results in this bytecode 在我的机器上,使用Java 1.8.0_201,您的代码段会生成此字节码

L0
 LINENUMBER 13 L0
 LDC "First"
 ASTORE 1
L1
 LINENUMBER 14 L1
 LDC "Second"
 ASTORE 2
L2
 LINENUMBER 15 L2
 LDC "Third"
 ASTORE 3
L3
 LINENUMBER 16 L3
 NEW java/lang/StringBuilder
 DUP
 INVOKESPECIAL java/lang/StringBuilder.<init> ()V
 ALOAD 1
 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 ALOAD 2
 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 ALOAD 3
 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
 INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
 ASTORE 4

which proves that 5 objects are being created (3 String literals*, 1 StringBuilder , 1 dynamically produced String instance by StringBuilder#toString ). 这证明正在创建5个对象 (3个String文字*,1个StringBuilder ,1个由StringBuilder#toString动态生成的String实例)。

Java 12 Java 12

On my machine, with Java 12.0.2, the bytecode is 在我的机器上,使用Java 12.0.2,字节码是

// identical to the bytecode above
L3
 LINENUMBER 16 L3
 ALOAD 1
 ALOAD 2
 ALOAD 3
 INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
  // handle kind 0x6 : INVOKESTATIC
  java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  // arguments:
  "\u0001\u0001\u0001"
 ]
 ASTORE 4

which magically changes "the correct answer" to 4 objects since there is no intermediate StringBuilder involved. 神奇地改变了4个对象的 “正确答案”,因为没有涉及中间的StringBuilder


*Let's dig a bit deeper. *让我们深入挖掘一下。

12.5. 12.5。 Creation of New Class Instances 创建新的类实例

A new class instance may be implicitly created in the following situations: 在以下情况下可能会隐式创建新的类实例:

  • Loading of a class or interface that contains a string literal ( §3.10.5 ) may create a new String object to represent the literal. 加载包含字符串文字(第3.10.5节 )的类或接口可能会创建一个新的String对象来表示文字。 (This will not occur if a string denoting the same sequence of Unicode code points has previously been interned.) (如果先前已经实现了表示相同Unicode代码点序列的字符串,则不会发生这种情况。)

In other words, when you start an application, there are already objects in the String pool. 换句话说,当您启动应用程序时,String池中已有对象。 You barely know what they are and where they come from (unless you scan all loaded classes for all literals they contain). 你几乎不知道它们是什么以及它们来自哪里(除非你扫描它们包含的所有文字的所有加载类)。

The java.lang.String class will be undoubtedly loaded as an essential JVM class, meaning all its literals will be created and placed into the pool. 毫无疑问, java.lang.String类作为一个基本的JVM类加载,这意味着它的所有文字都将被创建并放入池中。

Let's take a randomly selected snippet from the source code of String , pick a couple of literals from it, put a breakpoint at the very beginning of our programme, and examine if the pool contains these literals. 让我们从String的源代码中随机选择一个片段,从中选择几个文字,在程序的最开头放一个断点,然后检查池是否包含这些文字。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    ...
    public String repeat(int count) {
        // ... 
        if (Integer.MAX_VALUE / count < len) {
            throw new OutOfMemoryError("Repeating " + len + " bytes String " + count +
                    " times will produce a String exceeding maximum size.");
        }
    }
    ...
}

They are there indeed. 他们确实存在。

As an interesting find, this IDEA's filtering has a side effect: the substrings I was looking for have been added to the pool as well. 作为一个有趣的发现,这个IDEA的过滤有副作用:我正在寻找的子串也被添加到池中。 The pool size increased by one ( "bytes String" was added) after I applied this.contains("bytes String") . 应用this.contains("bytes String")后,池大小增加了一个( "bytes String"已添加this.contains("bytes String")

Where does this leave us? 这给我们留下了什么?

We have no idea whether "First" was created and interned before we call String str1 = "First"; 在调用String str1 = "First";之前,我们不知道是否创建了"First"并进行了实习String str1 = "First"; , so we can't state firmly that the line creates a new instance. ,所以我们不能坚定地说该行创建了一个新实例。

With the given information, the question cannot be definitely answered. 根据给定的信息,问题无法得到明确的回答。 As is stated in the JLS, §15.18.1 : JLS中所述,§15.18.1

... To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression. ...为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少通过表达式求值创建的中间String对象的数量。

This means that the answer depends at least on the concrete Java compiler used. 这意味着答案至少取决于所使用的具体Java编译器。

I think the best we can do is give an interval as answer: 我认为我们能做的最好的就是给出一个间隔作为答案:

  • a smart compiler may be able to infer that str1 to str3 are never used and fold the concatenation during compilation, such that only one String -object is created (the one referenced by str4 ) 智能编译器可能能够推断从不使用str1str3并在编译期间折叠连接,这样只创建一个String str4str4引用的str4
  • The maximum sensible number of String s created should be 5: one each for str1 to str3 , one for tmp = str1 + str2 and one for str4 = tmp + str3 . 创建的String的最大合理数量应为5: str1str3各一个, tmp = str1 + str2一个, str4 = tmp + str3

So... my answer would be "something between one to five String -objects". 所以...我的答案是“一到五个String -objects之间的东西”。 As to the total number of objects created just for this operation... I do not know. 至于为此操作创建的对象总数...我不知道。 This may also depend how exactly eg StringBuffer is implemented. 这也可能取决于如何实现StringBuffer

As an aside: I wonder what the reason behind asking such questions is. 暂且不说:我想知道问这些问题背后的原因是什么。 Normally, one does not need to care about those details. 通常,人们不需要关心这些细节。

Java 8 will likely create 5 objects: Java 8可能会创建5个对象:

  • 3 for the 3 literals 3为3文字
  • 1 StringBuilder 1 StringBuilder
  • 1 for the concatenated String 1用于连接String

With Java 9 things changed though and String concatenation does not use StringBuilder anymore. 使用Java 9时, 事情发生了变化String连接不再使用StringBuilder

It should be 5: 它应该是5:

  • three for the three literals (assigned to str1 , str2 and str3 ) 三个文字的三个(分配给str1str2str3

  • one for str1 + str2 一个用于str1 + str2

  • one for (result from the previous operation) + str3 (assigned to str4 ) 一个用于(result from the previous operation) + str3 (分配给str4

符合条件的Java实现可以在运行时或编译时以任意数量的方式连接字符串,需要任意数量的运行时对象,如果它检测到运行时不需要结果,则包括零对象。

4 string object will be created in string constant pool. 将在字符串常量池中创建4个字符串对象。 3 for literals and 1 with concatenation. 3用于文字,1用于连接。

if we use 如果我们使用

String s1 = new String("one")

it will create two object one in constant pool and one in heap memory. 它将在常量池中创建两个对象,在堆内存中创建一个。

if we define : 如果我们定义:

String s1 = "one";
String s2 = new String("one");

it will create two object one in constant pool and one in heap memory. 它将在常量池中创建两个对象,在堆内存中创建一个。

Concatenation operation doesn't create those many String objects. 连接操作不会创建那么多String对象。 It creates a StringBuilder and then appends the strings. 它创建一个StringBuilder ,然后附加字符串。 So there may be 5 objects, 3 (variables) + 1 (sb) + 1 (Concatenated string). 所以可能有5个对象,3个(变量)+ 1个(sb)+ 1个(连接字符串)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM