web-dev-qa-db-ger.com

Wie viele String-Objekte würden beim Verketten mehrerer Strings erstellt?

Ich wurde in einem Interview nach der Anzahl der Objekte gefragt, die für das gegebene Problem erstellt werden:

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

Ich antwortete, dass 6 Objekte im String-Pool erstellt werden würde.

3 wäre für jede der drei Variablen.
1 wäre für str1 + str2 (sagen wir str).
1 wäre für str2 + str3.
1 wäre für die str + str3 (str = str1 + str2).

Ist die Antwort, die ich gegeben habe, richtig? Wenn nicht, wie lautet die richtige Antwort?

36
bhpsh

Jede Antwort auf Ihre Frage hängt von der JVM-Implementierung und der aktuell verwendeten Version Java ab. Ich denke, es ist eine unvernünftige Frage, die man in einem Interview stellt.

Java 8

Auf meinem Computer führt Ihr Snippet mit Java 1.8.0_201 zu diesem Bytecode

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

dies beweist, dass 5 Objekte erstellt werden (3 String Literale *, 1 StringBuilder =, 1 dynamisch erzeugte String Instanz von StringBuilder#toString ).

Java 12

Auf meinem Computer lautet der Bytecode mit 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

welches magisch "die richtige Antwort" in 4 Objekte ändert, da kein Zwischenprodukt StringBuilder beteiligt ist.


* Lass uns etwas tiefer graben.

12.5. Erstellung neuer Klasseninstanzen

In den folgenden Situationen kann implizit eine neue Klasseninstanz erstellt werden:

  • Durch das Laden einer Klasse oder Schnittstelle, die ein Zeichenfolgenliteral enthält ( §3.10.5 ), wird möglicherweise ein neues Zeichenfolgenobjekt erstellt, das das Literal darstellt. (Dies tritt nicht auf, wenn zuvor eine Zeichenfolge interniert wurde, die dieselbe Folge von Unicode-Codepunkten angibt.)

Mit anderen Worten, wenn Sie eine Anwendung starten, befinden sich bereits Objekte im Zeichenfolgenpool. Sie wissen kaum, was sie sind und woher sie kommen (es sei denn, Sie scannen alle geladenen Klassen nach allen darin enthaltenen Literalen).

Die Klasse Java.lang.String Wird zweifellos als wesentliche JVM-Klasse geladen, dh alle ihre Literale werden erstellt und in den Pool gestellt.

Nehmen wir ein zufällig ausgewähltes Snippet aus dem Quellcode von String, wählen Sie ein paar Literale aus, setzen Sie einen Haltepunkt ganz am Anfang unseres Programms und prüfen Sie, ob der Pool diese Literale enthält.

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.");
        }
    }
    ...
}

Sie sind tatsächlich da.

Als interessanter Fund hat die Filterung dieser IDEA einen Nebeneffekt: Die von mir gesuchten Teilzeichenfolgen wurden ebenfalls dem Pool hinzugefügt. Die Poolgröße wurde um eins erhöht ("bytes String" Wurde hinzugefügt), nachdem ich this.contains("bytes String") angewendet habe.

Wo bleibt uns das?

Wir haben keine Ahnung, ob "First" Vor dem Aufruf von String str1 = "First"; Erstellt und interniert wurde, daher können wir nicht sicher sagen, dass die Zeile eine neue Instanz erstellt.

31
Andrew Tobilko

Mit den gegebenen Informationen kann die Frage nicht definitiv beantwortet werden. Wie in der JLS angegeben, §15.18.1 :

... Um die Leistung der wiederholten Verkettung von Zeichenfolgen zu erhöhen, kann ein Java -Compiler die Klasse StringBuffer oder eine ähnliche Technik verwenden, um die Anzahl der von erstellten Zwischenzeichenfolgenobjekte zu verringern Auswertung eines Ausdrucks.

Dies bedeutet, dass die Antwort zumindest vom konkreten Java -Compiler) abhängt.

Ich denke, das Beste, was wir tun können, ist, ein Intervall als Antwort zu geben:

  • ein intelligenter Compiler kann möglicherweise darauf schließen, dass str1 bis str3 niemals verwendet werden, und die Verkettung während der Kompilierung falten, sodass nur ein String- Objekt erstellt wird (dasjenige, auf das verwiesen wird) von str4)
  • Die maximal sinnvolle Anzahl der erstellten Strings sollte 5 betragen: jeweils eine für str1 Bis str3, Eine für tmp = str1 + str2 Und eine für str4 = tmp + str3 .

Also ... meine Antwort wäre "etwas zwischen einem und fünf String- Objekten". Was die Gesamtzahl der Objekte betrifft, die nur für diesen Vorgang erstellt wurden ... weiß ich nicht. Dies kann auch davon abhängen, wie genau z. StringBuffer ist implementiert.

Nebenbei: Ich frage mich, warum solche Fragen gestellt wurden. Normalerweise muss man sich nicht um diese Details kümmern.

18
Turing85

Java 8 wird wahrscheinlich 5 Objekte erstellen:

  • 3 für die 3 Literale
  • 1 StringBuilder
  • 1 für das verkettete String

Mit Java 9 Dinge geändert == und String Verkettung verwendet StringBuilder nicht mehr.

8
Puce

Es sollte 5 sein:

  • drei für die drei Literale (zugewiesen an str1, str2 und str3)

  • eins für str1 + str2

  • eins für (result from the previous operation) + str3 (Zugewiesen an str4)

4
Laurenz Albe

Eine konforme Java -Implementierung kann die Zeichenfolgen zur Laufzeit oder zur Kompilierungszeit auf beliebig viele Arten verketten und benötigt eine beliebige Anzahl von Laufzeitobjekten, einschließlich Nullobjekten, wenn festgestellt wird, dass das Ergebnis nicht vorliegt zur Laufzeit benötigt.

3
Boann

Im String-Konstantenpool werden 4 String-Objekte erstellt. 3 für Literale und 1 für Verkettung.

wenn wir verwenden

String s1 = new String("one")

es werden zwei Objekte erstellt, eines im konstanten Pool und eines im Heapspeicher.

wenn wir definieren:

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

es werden zwei Objekte erstellt, eines im konstanten Pool und eines im Heapspeicher.

2
Parmar Kamlesh

Durch die Verkettungsoperation werden nicht so viele String-Objekte erstellt. Es erstellt ein StringBuilder und hängt dann die Zeichenfolgen an. Es kann also 5 Objekte geben, 3 (Variablen) + 1 (sb) + 1 (verkettete Zeichenfolge).

1
javaaddict