Wann sollten wir die interne Methode von String für String-Literale verwenden?

187

Laut String # intern () soll die internMethode den String aus dem String-Pool zurückgeben, wenn der String im String-Pool gefunden wird. Andernfalls wird ein neues String-Objekt im String-Pool hinzugefügt und die Referenz dieses Strings zurückgegeben.

Also habe ich das versucht:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}

Ich hatte erwartet, dass s1 and s3 are samedas gedruckt wird, wenn s3 interniert ist, und s1 and s2 are samenicht gedruckt wird. Das Ergebnis ist jedoch: Beide Zeilen werden gedruckt. Das heißt, standardmäßig werden String-Konstanten interniert. Aber wenn ja, warum brauchen wir dann die internMethode? Mit anderen Worten, wann sollten wir diese Methode anwenden?

Rakesh Juyal
quelle
14
In dem von Ihnen verknüpften Javadoc heißt es außerdem: "Alle Literalzeichenfolgen und konstanten Ausdrücke mit Zeichenfolgenwerten werden interniert."
Jorn
1
kein genaues Duplikat ..
Bozho
1
@Jorn: das stimmt. Warum haben wir also interneine öffentliche Methode? Sollten wir nicht internals private Methode haben, damit niemand Zugriff darauf haben könnte. Oder gibt es einen Zweck dieser Methode?
Rakesh Juyal
2
@RakeshJuyal: Die interne Methode wird für einen Zeichenfolgentyp definiert, der Zeichenfolgenliterale oder -variablen sein kann. Wie würden Sie eine Variable internieren, wenn die Methode privat wäre?
Bobbyalex

Antworten:

230

Java interniert automatisch String-Literale. Dies bedeutet, dass in vielen Fällen der Operator == für Strings genauso zu funktionieren scheint wie für Ints oder andere primitive Werte.

Da die Internierung für String-Literale automatisch erfolgt, ist die intern()Methode für Strings anzuwenden, die mit erstellt wurdennew String()

Anhand Ihres Beispiels:

String s1 = "Rakesh";
String s2 = "Rakesh";
String s3 = "Rakesh".intern();
String s4 = new String("Rakesh");
String s5 = new String("Rakesh").intern();

if ( s1 == s2 ){
    System.out.println("s1 and s2 are same");  // 1.
}

if ( s1 == s3 ){
    System.out.println("s1 and s3 are same" );  // 2.
}

if ( s1 == s4 ){
    System.out.println("s1 and s4 are same" );  // 3.
}

if ( s1 == s5 ){
    System.out.println("s1 and s5 are same" );  // 4.
}

wird zurückkehren:

s1 and s2 are same
s1 and s3 are same
s1 and s5 are same

In allen Fällen außer der s4Variablen, für die ein Wert explizit mit dem newOperator erstellt wurde und für internderen Ergebnis keine Methode verwendet wurde, handelt es sich um eine einzelne unveränderliche Instanz, die an den String-Konstantenpool von JVM zurückgegeben wird .

Weitere Informationen finden Sie unter JavaTechniques "String Equality and Interning" .

Filipe Miguel Fonseca
quelle
Ich gehe davon aus, dass Java String-Literale zu Optimierungszwecken automatisch interniert. Es kann dies nur sicher tun, weil Strings unveränderlich sind, richtig?
Styfle
Neu in Java (ich komme aus der C # .NET-Welt) und manchmal sehe ich in einem Java-Legacy-Projekt "" .intern (). Wenn ich es richtig verstehe, ist dies "Unsinn" auch für leere Zeichenfolgen.
hfrmobile
4
@ Miguel Schöne Erklärung, meine Frage ist, wie hier in Ihrem Beispiel Objekt erstellt werden kann. Hier ist meine Vermutung: String s1 = "Rakesh"; erster OB1 String s4 = new String("Rakesh");Zweite OB2 So Rest (s2, s3, s5) Referenz gleiches Objekt (OB 1) erstellt in ‚string Pool‘ So kann ich sagen , dass .intern()Verfahren zum Verhindern , dass verwendeten neues Objekt zu erstellen , wenn gleiche Zeichenfolge in string poolFalls Meine Annahme ist falsch, also gib mir die Richtung.
HybrisHelp
1
Der JavaTechniques-Link ist unterbrochen
SJuan76
20

In einem kürzlich durchgeführten Projekt wurden einige große Datenstrukturen mit Daten eingerichtet, die aus einer Datenbank eingelesen wurden (und daher keine String-Konstanten / Literale), jedoch mit einer großen Menge an Duplikaten. Es war eine Bankanwendung, und Dinge wie die Namen einer bescheidenen Gruppe (vielleicht 100 oder 200) tauchten überall auf. Die Datenstrukturen waren bereits groß, und wenn alle diese Unternehmensnamen eindeutige Objekte gewesen wären, wäre der Speicher übergelaufen. Stattdessen hatten alle Datenstrukturen Verweise auf dieselben 100 oder 200 String-Objekte, wodurch viel Platz gespart wurde.

Ein weiterer kleiner Vorteil von internierten Strings besteht darin, ==dass sie (erfolgreich!) Zum Vergleichen von Strings verwendet werden können, wenn garantiert ist, dass alle beteiligten Strings interniert sind. Neben der schlankeren Syntax ist dies auch eine Leistungssteigerung. Aber wie andere haben darauf hingewiesen, dies zu tun Häfen ein großes Risiko von Programmierfehlern einzuführen, so sollte dies nur als desparate Maß der letzten Instanz durchgeführt werden.

Der Nachteil ist, dass das Internieren eines Strings mehr Zeit in Anspruch nimmt als das einfache Werfen auf den Heap, und dass der Speicherplatz für internierte Strings je nach Java-Implementierung begrenzt sein kann. Dies geschieht am besten, wenn Sie mit einer bekannten vernünftigen Anzahl von Zeichenfolgen mit vielen Duplikaten zu tun haben.

Carl Smotricz
quelle
@ The downside is that interning a String takes more time than simply throwing it on the heap, and that the space for interned Strings may be limitedAuch wenn Sie die interne Methode für die String-Konstante nicht verwenden, wird sie automatisch interniert.
Rakesh Juyal
2
@ Rakesh: Es gibt normalerweise nicht so viele String-Konstanten in einer bestimmten Klasse, daher ist es kein Problem von Raum / Zeit mit Konstanten.
David Rodríguez - Dribeas
Ja, Rakeshs Kommentar trifft nicht zu, da das Internieren von Strings nur (explizit) mit Strings erfolgt, die irgendwie "generiert" werden, sei es durch interne Manipulation oder durch Abrufen aus einer Datenbank oder dergleichen. Mit Konstanten haben wir keine Wahl.
Carl Smotricz
2
+1. Ich denke, dies ist ein gutes Beispiel dafür, wann ein Praktikum Sinn macht. Ich stimme jedoch nicht ==für Saiten zu.
Alexander Pogrebnyak
1
Ab Java 7 wird der "String-Pool" in den Heap-Speicher implementiert, sodass alle Vorteile des Speicherns von Praktikanten und der Speicherbereinigung genutzt werden. Die Größe ist nicht begrenzt. Er kann auf die Heap-Größe erhöht werden (Sie werden nie so viel benötigen Speicher für Streicher)
Anil Uttani
15

Ich möchte meine 2 Cent für die Verwendung ==mit internierten Zeichenfolgen hinzufügen .

Das erste, was String.equalstut, ist this==object.

Obwohl es einen winzigen Leistungsgewinn gibt (Sie rufen keine Methode auf), ist die Verwendung aus Sicht des Betreuers ==ein Albtraum, da einige internierte Zeichenfolgen dazu neigen, nicht interniert zu werden.

Daher empfehle ich, sich nicht auf den Sonderfall ==für internierte Saiten zu verlassen, sondern immer so zu verwenden, equalswie es Gosling beabsichtigt hat.

EDIT: interniert wird nicht interniert:

V1.0
public class MyClass
{
  private String reference_val;

  ...

  private boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}

In Version 2.0 hat sich der Betreuer entschlossen, hasReferenceValöffentlich zu machen , ohne ins Detail zu gehen, dass er eine Reihe von internierten Zeichenfolgen erwartet.

V2.0
public class MyClass
{
  private String reference_val;

  ...

  public boolean hasReferenceVal ( final String[] strings )
  {
    for ( String s : strings )
    {
      if ( s == reference_val )
      {
        return true;
      }
    }

    return false;
  }

  private void makeCall ( )
  {
     final String[] interned_strings =  { ... init with interned values ... };

     if ( hasReference( interned_strings ) )
     {
        ...
     }
  }
}

Jetzt haben Sie einen Fehler, der möglicherweise sehr schwer zu finden ist, da das Array in den meisten Fällen Literalwerte enthält und manchmal eine nicht-wörtliche Zeichenfolge verwendet wird. Wenn equalsstatt verwendet wurden , ==dann hasReferenceValwürde noch weiter haben zu arbeiten. Auch hier ist der Leistungsgewinn winzig, aber die Wartungskosten sind hoch.

Alexander Pogrebnyak
quelle
"Einige internierte Saiten neigen dazu, nicht interniert zu werden." wow, das wäre ... seltsam. Können Sie bitte eine Referenz zitieren?
Carl Smotricz
2
OK, ich dachte, Sie beziehen sich auf Strings, die dank der Magie in der JVM tatsächlich aus dem internen Pool auf den Haufen wandern. Was Sie sagen ist, dass == bestimmte Klassen von Programmiererfehlern wahrscheinlicher macht.
Carl Smotricz
"Ich schlage daher vor, sich bei internierten Zeichenfolgen nicht auf den Sonderfall == zu verlassen, sondern immer gleich zu verwenden, wie Gosling es beabsichtigt hat." Haben Sie ein direktes Zitat oder einen Kommentar von Gosling, der dies besagt? Wenn das der Fall ist, warum hat er sich überhaupt die Mühe gemacht, intern () und die Verwendung von == in die Sprache zu setzen?
1
intern ist nicht gut für den direkten Vergleich (==), obwohl es funktioniert, wenn beide Zeichenfolgen interniert sind. Es ist großartig, den insgesamt verwendeten Speicher zu verringern: Wenn dieselbe Zeichenfolge an mehr als einer Stelle verwendet wird.
Tgkprog
12

String-Literale und -Konstanten werden standardmäßig interniert. Das heißt, "foo" == "foo"(deklariert durch die String-Literale), aber new String("foo") != new String("foo").

Bozho
quelle
4
Die Frage ist also, wann wir verwenden sollten intern,
Rakesh Juyal
Das war auf stackoverflow.com/questions/1833581/when-to-use-intern und eine Reihe anderer Fragen gerichtet, einige davon von gestern.
Bozho
Lassen Sie mich wissen, ob mein Verständnis für diese Aussage String literals and constants are interned by defaultrichtig ist. new String("foo")-> Hier wird ein String-Literal "foo" im String-Pool und eines im Heap erstellt, sodass insgesamt 2 Objekte erstellt werden.
dkb
8

Lernen Sie Java String Intern - ein für allemal

Strings in Java sind von Natur aus unveränderliche Objekte. Daher sind zwei Zeichenfolgenobjekte auch mit demselben Wert standardmäßig unterschiedliche Objekte. Wenn wir jedoch Speicherplatz sparen möchten, können wir angeben, dass derselbe Speicher von einem Konzept namens string intern verwendet wird.

Die folgenden Regeln helfen Ihnen dabei, das Konzept klar zu verstehen:

  1. Die String-Klasse verwaltet einen internen Pool, der anfangs leer ist. Dieser Pool muss garantieren, dass Zeichenfolgenobjekte nur eindeutige Werte enthalten.
  2. Alle Zeichenfolgenliterale mit demselben Wert müssen als dasselbe Speicherortobjekt betrachtet werden, da sie ansonsten keinen Unterscheidungsbegriff haben. Daher machen alle diese Literale mit demselben Wert einen einzelnen Eintrag im internen Pool und verweisen auf denselben Speicherort.
  3. Die Verkettung von zwei oder mehr Literalen ist ebenfalls ein Literal. (Daher gilt Regel 2 für sie.)
  4. Jede als Objekt erstellte Zeichenfolge (dh mit einer anderen Methode als Literal) hat unterschiedliche Speicherorte und nimmt keinen Eintrag im internen Pool vor
  5. Die Verkettung von Literalen mit Nicht-Literalen ergibt ein Nicht-Literal. Somit hat das resultierende Objekt einen neuen Speicherort und nimmt KEINEN Eintrag im internen Pool vor.
  6. Wenn Sie die interne Methode für ein Zeichenfolgenobjekt aufrufen, wird entweder ein neues Objekt erstellt, das in den internen Pool aufgenommen wird, oder es wird ein vorhandenes Objekt aus dem Pool zurückgegeben, das denselben Wert hat. Durch den Aufruf eines Objekts, das sich nicht im internen Pool befindet, wird das Objekt NICHT in den Pool verschoben. Es wird vielmehr ein anderes Objekt erstellt, das in den Pool gelangt.

Beispiel:

String s1=new String (“abc”);
String s2=new String (“abc”);
If (s1==s2)  //would return false  by rule #4
If (“abc == a”+”bc )  //would return true by rules #2 and #3
If (“abc == s1 )  //would return false  by rules #1,2 and #4
If (“abc == s1.intern() )  //would return true  by rules #1,2,4 and #6
If ( s1 == s2.intern() )      //wound return false by rules #1,4, and #6

Hinweis: Die Motivationsfälle für String-Praktikanten werden hier nicht behandelt. Das Speichern von Speicher wird jedoch definitiv eines der Hauptziele sein.

KGhatak
quelle
Vielen Dank für # 3, ich wusste es nicht :)
Kaay
4

Sie sollten zwei Zeiträume erkennen, die Kompilierungszeit und Laufzeit sind. Zum Beispiel:

//example 1 
"test" == "test" // --> true 
"test" == "te" + "st" // --> true

//example 2 
"test" == "!test".substring(1) // --> false
"test" == "!test".substring(1).intern() // --> true

Einerseits stellen wir in Beispiel 1 fest, dass die Ergebnisse alle true zurückgeben, da der JVM in der Kompilierungszeit den "Test" in den Pool der Literalzeichenfolgen einfügt, wenn der JVM-Find "Test" vorhanden ist Es wird die vorhandene verwendet. In Beispiel 1 zeigen die "Test" -Strings alle auf dieselbe Speicheradresse, sodass in Beispiel 1 true zurückgegeben wird. Andererseits wird in Beispiel 2 die Methode von substring () zur Laufzeit ausgeführt. Im Fall von "test" == "! test" .substring (1) erstellt der Pool zwei Zeichenfolgenobjekte. " test "und"! test ", also sind sie verschiedene Referenzobjekte, daher gibt dieser Fall false zurück, im Fall von" test "=="! test ".substring (1) .intern (), der Methode von intern ( ) setzt den ""! test ".substring (1)" in den Pool der wörtlichen Zeichenfolgen,

thinkinjava
quelle
3

http://en.wikipedia.org/wiki/String_interning

String-Internierung ist eine Methode zum Speichern nur einer Kopie jedes einzelnen String-Werts, der unveränderlich sein muss. Durch das Internieren von Zeichenfolgen werden einige Zeichenfolgenverarbeitungsaufgaben zeit- oder platzsparender, und es wird mehr Zeit benötigt, wenn die Zeichenfolge erstellt oder interniert wird. Die unterschiedlichen Werte werden in einem internen String-Pool gespeichert.

bbcbcgb
quelle
2

Internierte Strings vermeiden doppelte Strings. Durch das Internieren wird RAM auf Kosten einer höheren CPU-Zeit gespart, um doppelte Strings zu erkennen und zu ersetzen. Es gibt nur eine Kopie jeder Zeichenfolge, die interniert wurde, unabhängig davon, wie viele Verweise darauf verweisen. Da Strings unveränderlich sind, können zwei verschiedene Methoden, die zufällig denselben String verwenden, eine Kopie desselben Strings gemeinsam nutzen. Das Konvertieren doppelter Strings in gemeinsam genutzte Strings heißt interning.String.intern () gibt Ihnen die Adresse des kanonischen Master- Strings an . Sie können internierte Strings mit simple == (das Zeiger vergleicht) anstelle von equals vergleichenHier werden die Zeichen des Strings einzeln verglichen. Da Strings unveränderlich sind, kann der interne Prozess Platz sparen, indem beispielsweise kein separates String-Literal für "pot" erstellt wird, wenn es als Teilzeichenfolge eines anderen Literals wie "Hippopotamus" vorhanden ist.

Weitere Informationen finden Sie unter http://mindprod.com/jgloss/interned.html

inter18099
quelle
2
String s1 = "Anish";
        String s2 = "Anish";

        String s3 = new String("Anish");

        /*
         * When the intern method is invoked, if the pool already contains a
         * string equal to this String object as determined by the
         * method, then the string from the pool is
         * returned. Otherwise, this String object is added to the
         * pool and a reference to this String object is returned.
         */
        String s4 = new String("Anish").intern();
        if (s1 == s2) {
            System.out.println("s1 and s2 are same");
        }

        if (s1 == s3) {
            System.out.println("s1 and s3 are same");
        }

        if (s1 == s4) {
            System.out.println("s1 and s4 are same");
        }

AUSGABE

s1 and s2 are same
s1 and s4 are same
Anish
quelle
2
String p1 = "example";
String p2 = "example";
String p3 = "example".intern();
String p4 = p2.intern();
String p5 = new String(p3);
String p6 = new String("example");
String p7 = p6.intern();

if (p1 == p2)
    System.out.println("p1 and p2 are the same");
if (p1 == p3)
    System.out.println("p1 and p3 are the same");
if (p1 == p4)
    System.out.println("p1 and p4 are the same");
if (p1 == p5)
    System.out.println("p1 and p5 are the same");
if (p1 == p6)
    System.out.println("p1 and p6 are the same");
if (p1 == p6.intern())
    System.out.println("p1 and p6 are the same when intern is used");
if (p1 == p7)
    System.out.println("p1 and p7 are the same");

Wenn zwei Zeichenfolgen unabhängig voneinander erstellt werden, intern()können Sie sie vergleichen und eine Referenz im Zeichenfolgenpool erstellen, wenn die Referenz zuvor nicht vorhanden war.

Wenn Sie verwenden String s = new String(hi), erstellt Java eine neue Instanz der Zeichenfolge. Wenn Sie jedoch verwenden String s = "hi", prüft Java, ob der Code eine Instanz des Wortes "hi" enthält oder nicht, und wenn es vorhanden ist, gibt es nur die Referenz zurück.

Da das Vergleichen von Zeichenfolgen auf Referenz basiert, können intern()Sie eine Referenz erstellen und den Inhalt der Zeichenfolgen vergleichen.

Wenn Sie intern()den Code verwenden, wird der von der Zeichenfolge verwendete Speicherplatz gelöscht, der auf dasselbe Objekt verweist, und es wird nur die Referenz des bereits vorhandenen Objekts im Speicher zurückgegeben.

Aber im Fall von p5, wenn Sie verwenden:

String p5 = new String(p3);

Es wird nur der Inhalt von p3 kopiert und p5 neu erstellt. Es ist also nicht interniert .

Die Ausgabe lautet also:

p1 and p2 are the same
p1 and p3 are the same
p1 and p4 are the same
p1 and p6 are the same when intern is used
p1 and p7 are the same
Raja Emani
quelle
2
    public static void main(String[] args) {
    // TODO Auto-generated method stub
    String s1 = "test";
    String s2 = new String("test");
    System.out.println(s1==s2);              //false
    System.out.println(s1==s2.intern());    //true --> because this time compiler is checking from string constant pool.
}
Anurag_BEHS
quelle
1

Die Methode string intern () wird verwendet, um eine exakte Kopie des Heap-String-Objekts im String-Konstantenpool zu erstellen. Die Zeichenfolgenobjekte im Zeichenfolgenkonstantenpool werden automatisch interniert, Zeichenfolgenobjekte im Heap jedoch nicht. Die Hauptanwendung beim Erstellen von Praktikanten besteht darin, Speicherplatz zu sparen und einen schnelleren Vergleich von Zeichenfolgenobjekten durchzuführen.

Quelle: Was ist String Intern in Java?

user2485429
quelle
1

Wie Sie sagten, wird diese Zeichenfolgenmethode intern()zuerst aus dem Zeichenfolgenpool gefunden. Wenn sie gefunden wird, gibt sie das Objekt zurück, das darauf verweist, oder fügt dem Pool eine neue Zeichenfolge hinzu.

    String s1 = "Hello";
    String s2 = "Hello";
    String s3 = "Hello".intern();
    String s4 = new String("Hello");

    System.out.println(s1 == s2);//true
    System.out.println(s1 == s3);//true
    System.out.println(s1 == s4.intern());//true

Das s1und s2sind zwei Objekte, die auf den String-Pool "Hallo" zeigen, und mit "Hello".intern()wird das s1und gefunden s2. Also "s1 == s3"kehrt true zurück, ebenso wie zum s3.intern().

WendellWu
quelle
Dies liefert nicht wirklich viele neue Informationen. Es gibt bereits eine außergewöhnliche Antwort.
Alexander
0

Wenn wir die Heap-Objektreferenz verwenden, um die entsprechende Objektreferenz für den String-Konstantenpool zu erhalten , sollten wir uns für intern () entscheiden.

String s1 = new String("Rakesh");
String s2 = s1.intern();
String s3 = "Rakesh";

System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true

Bildansicht Geben Sie hier die Bildbeschreibung ein

Schritt 1: Objekt mit Daten 'Rakesh' wird im Heap- und String-Konstantenpool erstellt. Außerdem zeigt s1 immer auf ein Heap-Objekt.

Schritt 2: Mit der Heap-Objektreferenz s1 versuchen wir, die interne String-Konstanten-Pool-Objektreferenz s2 mit intern () abzurufen.

Schritt 3: Absichtlich ein Objekt mit den Daten 'Rakesh' im String-Konstanten-Pool erstellen, auf das mit dem Namen s3 verwiesen wird

Als "==" Operator für Referenzvergleich gedacht.

Erste falsch für s1 == s2

Erste wahr für s2 == s3

Ich hoffe das hilft!!

Kms
quelle