Ich habe eine Frage zu einem seltsamen String-Pool-Verhalten. Ich benutze == um gleiche Strings zu vergleichen, um herauszufinden, ob sie im Pool sind oder nicht.
public class StringPoolTest {
public static void main(String[] args) {
new StringPoolTest().run();
}
String giveLiteralString() {
return "555";
}
void run() {
String s1 = giveLiteralString() + "";
System.out.println("555" == "555" + "");
System.out.println(giveLiteralString() == giveLiteralString() + "");
}
}
Die Ausgabe ist:
true
false
was für mich eine große Überraschung ist. Könnte das bitte jemand erklären? Ich denke, etwas darüber findet zur Kompilierzeit statt. Aber warum fügt hinzu "" zu einem String überhaupt einen Unterschied macht?
@MarkoTopolnik Scheint mir gleich.
– johnchen902
8. Juli 2013 um 11:18 Uhr
@MarkoTopolnik Ich wusste, dass die Frage etwas anders ist. Aber die Antwort ist immer wie “XXX ist eine Kompilierzeitkonstante, YYY dagegen nicht”. Vielleicht hatte ich aber auch eine falsche Frage gewählt.
– johnchen902
8. Juli 2013 um 11:22 Uhr
@johnchen902 Ich stimme zu, aber du hast die falsche Frage als Duplikat gepostet 🙂
– Thihara
8. Juli 2013 um 12:25 Uhr
Möchten Sie wirklich Referenzen oder die zurückgegebenen Zeichenfolgen vergleichen?
– Stute Infinitus
22. Juli 2013 um 11:17 Uhr
@MareInfinitus Ja, warum nicht? Es ist viel schneller als das Vergleichen mit Gleichheit. Natürlich müssen Sie sicher sein, dass alle Saiten im Pool sind (zB via intern()).
ist nicht. Daher kompiliert Ersteres nur in die Zeichenfolgenkonstante “555” und Letzteres kompiliert in den eigentlichen Methodenaufruf und die Verkettung, was zu einer neuen String-Instanz führt.
Strings, die zur Laufzeit durch Verkettung berechnet werden, werden neu erstellt und sind daher eindeutig.
Können Sie bitte ein Zitat für eine neue Zeichenfolgeninstanz im Falle eines Methodenaufrufs angeben? Irgendwelche JLS-Links?
– Sanbhat
8. Juli 2013 um 11:18 Uhr
Auch der Codeoptimierer, der vor/während der Kompilierung ausgeführt wird, kann bereits kombiniert werden "555"+"" zu einem einzelnen String-Objekt "555" Während method()+"" wird immer noch sein method()+"" nach Zusammenstellung.
– Koraschen
8. Juli 2013 um 11:19 Uhr
@sanbhat Klicken Sie in meiner Antwort auf “Kompilierzeitkonstante”.
– Marko Topolnik
8. Juli 2013 um 11:20 Uhr
Nebenfrage: Wenn Sie hinzufügen würden final zu giveLiteralString()würde es etwas ändern?
– Constantino Zarouhas
10. Juli 2013 um 11:05 Uhr
@RandyMarsh Es würde nicht: Sehen Sie sich die Liste der zulässigen Ausdrücke für Konstanten zur Kompilierzeit an.
if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) {
...
was immer false ergibt, da wir hier 2 unterschiedliche Objekte vergleichen.
“was immer false produziert” — Technisch gesehen ist StringBuilder das nicht erforderlich um eine nicht-internierte Zeichenfolge zu erzeugen. Es gibt einfach keinen guten Grund dafür, zu versuchen, eine internierte zu erstellen.
– Heiße Licks
8. Juli 2013 um 11:29 Uhr
From StringBuilder.toString API – Ein neues String-Objekt wird zugewiesen und initialisiert, um die derzeit durch dieses Objekt dargestellte Zeichenfolge zu enthalten. Dieser String wird dann zurückgegeben.
– Evgeniy Dorofeev
8. Juli 2013 um 11:31 Uhr
Aber nichts sagt, dass es nicht interniert werden kann.
– Heiße Licks
8. Juli 2013 um 11:35 Uhr
@HotLicks Wenn es interniert wurde, kann nicht garantiert werden, dass es tatsächlich neu ist. Wenn der String-Pool diesen String bereits enthält, wird die alte Instanz von zurückgegeben intern.
– Marko Topolnik
8. Juli 2013 um 11:37 Uhr
@HotLicks Ich habe es gelesen, als ein neues String-Objekt zurückgegeben wird. Das ist immer neu, kein Objekt aus Pool
– Evgeniy Dorofeev
8. Juli 2013 um 11:39 Uhr
Im zweiten Fall hätte der Compiler das erkennen können + "" ist eine Art No-Op, da "" ist ein Wert zur Kompilierzeit, der bekanntermaßen eine Länge von Null hat. Aber der Compiler ist immer noch erforderlich, um das Ergebnis zu überprüfen giveLiteralString für null (da die Nullprüfung als Ergebnis der + Betrieb im nicht optimierten Fall), daher ist es am einfachsten, die Optimierung einfach nicht zu versuchen.
Als Ergebnis generiert der Compiler Code zum Ausführen der Verkettung, und es wird eine neue Zeichenfolge erstellt.
Der Compiler muss dem JLS entsprechen, das ausdrücklich besagt, dass das Ergebnis der Laufzeitverkettung ein frischer String ist.
– Marko Topolnik
8. Juli 2013 um 11:39 Uhr
Compile Time Concatenation
Durch einen konstanten Ausdruck berechnete Zeichenfolgen werden zur Kompilierzeit ausgeführt und als Konstanten oder Literale behandelt, was bedeutet, dass der Wert der Zeichenfolge oder des Ausdrucks zur Kompilierzeit bekannt ist oder ausgewertet wird, sodass der Compiler denselben Wert im Zeichenfolgenpool überprüfen und dieselbe Zeichenfolgenobjektreferenz zurückgeben kann .
Laufzeitverkettung
Zeichenfolgenausdrücke, deren Werte bekannt sind oder beim Kompilieren nicht ausgewertet werden können, aber von der Eingabe oder Laufzeitbedingung abhängen, kennt der Compiler den Wert der Zeichenfolge nicht und verwendet daher immer StringBuilder, um die Zeichenfolge anzuhängen, und gibt immer a zurück neue Saite. Ich denke, dieses Beispiel wird es besser verdeutlichen.
public static void main(String[] args) {
new StringPoolTest().run();
}
String giveLiteralString() {
return "555";
}
void run() {
System.out.println("555" + 9 == "555" + 9);
System.out.println("555"+Integer.valueOf(9) == "555" + Integer.valueOf(9));
System.out.println(giveLiteralString() == giveLiteralString());
// The result of runtime concatenation is a fresh string.
System.out.println(giveLiteralString() == giveLiteralString() + "");
}
@MarkoTopolnik Scheint mir gleich.
– johnchen902
8. Juli 2013 um 11:18 Uhr
@MarkoTopolnik Ich wusste, dass die Frage etwas anders ist. Aber die Antwort ist immer wie “XXX ist eine Kompilierzeitkonstante, YYY dagegen nicht”. Vielleicht hatte ich aber auch eine falsche Frage gewählt.
– johnchen902
8. Juli 2013 um 11:22 Uhr
@johnchen902 Ich stimme zu, aber du hast die falsche Frage als Duplikat gepostet 🙂
– Thihara
8. Juli 2013 um 12:25 Uhr
Möchten Sie wirklich Referenzen oder die zurückgegebenen Zeichenfolgen vergleichen?
– Stute Infinitus
22. Juli 2013 um 11:17 Uhr
@MareInfinitus Ja, warum nicht? Es ist viel schneller als das Vergleichen mit Gleichheit. Natürlich müssen Sie sicher sein, dass alle Saiten im Pool sind (zB via
intern()
).– Joze
22. Juli 2013 um 12:02 Uhr