Quando dovremmo usare il metodo intern di String sui valori letterali di String


187

Secondo String # intern () , il internmetodo dovrebbe restituire la stringa dal pool String se la stringa viene trovata nel pool String, altrimenti un nuovo oggetto stringa verrà aggiunto nel pool String e verrà restituito il riferimento di questa stringa.

Quindi ho provato questo:

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.
}

Mi aspettavo che s1 and s3 are sameverrà stampato come s3 è internato e s1 and s2 are samenon verrà stampato. Ma il risultato è: entrambe le linee sono stampate. Ciò significa che, per impostazione predefinita, le costanti String vengono internate. Ma se è così, allora perché abbiamo bisogno del internmetodo? In altre parole, quando dovremmo usare questo metodo?


14
Il Javadoc che hai collegato afferma anche "Tutte le stringhe letterali e le espressioni costanti con valore di stringa vengono internate".
Jorn,


1
non un duplicato esatto ..
Bozho,

1
@Jorn: esatto. Quindi perché abbiamo interncome metodo pubblico. Non dovremmo avere internun metodo privato, in modo che nessuno possa accedervi. O c'è qualche scopo di questo metodo?
Rakesh Juyal,

2
@RakeshJuyal: il metodo intern è definito su un tipo di stringa che può essere letterale o variabile. Come useresti una variabile se il metodo fosse privato?
bobbyalex,

Risposte:


230

Java esegue internamente automaticamente valori letterali String. Ciò significa che in molti casi l'operatore == sembra funzionare per le stringhe nello stesso modo in cui lo fa per gli ints o altri valori primitivi.

Poiché interning è automatico per i letterali String, il intern()metodo deve essere utilizzato su stringhe costruite connew String()

Usando il tuo esempio:

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.
}

sarà di ritorno:

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

In tutti i casi oltre a s4variabile, un valore per il quale è stato creato esplicitamente tramite l' newoperatore e in cui il internmetodo non è stato utilizzato sul suo risultato, è una singola istanza immutabile che viene restituita dal pool di costanti di stringhe di JVM .

Fare riferimento a Tecniche Java "Uguaglianza delle stringhe e interning" per ulteriori informazioni.


Suppongo che Java esegua automaticamente internamenti dei letterali String per scopi di ottimizzazione. Può farlo in modo sicuro solo perché le stringhe sono immutabili, giusto?
Styfle

Nuovo su Java (vengo dal mondo C # .NET) e qualche volta vedo in un progetto legacy Java "" .intern (), quindi se capisco correttamente che questo è "senza senso" anche per stringhe vuote.
hfrmobile,

4
@Miguel Bella spiegazione, la mia domanda è come può un oggetto creato qui nel tuo esempio. Ecco il mio presupposto: String s1 = "Rakesh"; primo OB1 String s4 = new String("Rakesh");Secondo OB2 Quindi il resto di (s2, s3, s5) fa riferimento allo stesso oggetto (OB1) creato in 'string Pool' Quindi posso dire che il .intern()metodo utilizzato per impedire la creazione di un nuovo oggetto se la stessa stringa è disponibile in string poolIf la mia ipotesi è sbagliata, quindi dammi una direzione.
Hybris Help

1
Il link JavaTechniques è interrotto
SJuan76


20

In un recente progetto, sono state create alcune enormi strutture di dati con dati letti da un database (e quindi non costanti / valori letterali) ma con un'enorme quantità di duplicati. Era un'applicazione bancaria e cose come i nomi di un gruppo modesto (forse 100 o 200) apparvero dappertutto. Le strutture dei dati erano già di grandi dimensioni e se tutti i nomi di quei corpi fossero stati oggetti univoci avrebbero traboccato di memoria. Al contrario, tutte le strutture dati avevano riferimenti agli stessi oggetti String 100 o 200, risparmiando così molto spazio.

Un altro piccolo vantaggio delle stringhe internate è che ==può essere usato (con successo!) Per confrontare le stringhe se tutte le stringhe coinvolte sono garantite per essere internate. Oltre alla sintassi più snella, questo è anche un miglioramento delle prestazioni. Ma come altri hanno sottolineato, fare questo comporta un grande rischio di introdurre errori di programmazione, quindi questo dovrebbe essere fatto solo come misura disperata di ultima istanza.

Il rovescio della medaglia è che l'internamento di una stringa richiede più tempo rispetto al semplice lancio nell'heap e che lo spazio per le stringhe internate può essere limitato, a seconda dell'implementazione Java. È meglio farlo quando si ha a che fare con un numero ragionevole noto di stringhe con molte duplicazioni.


@ 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 limitedanche se non usi il metodo intern per la costante String, verrà internato automaticamente.
Rakesh Juyal,

2
@Rakesh: Di solito non ci sono molte costanti String in una data classe, quindi non è un problema di spazio / tempo con le costanti.
David Rodríguez - dribeas,

Sì, il commento di Rakesh non si applica perché internare le stringhe viene fatto solo (esplicitamente) con le stringhe che sono "generate" in qualche modo, sia attraverso la manipolazione interna o recuperando da un database o simili. Con le costanti non abbiamo scelta.
Carl Smotricz,

2
+1. Penso che questo sia un buon esempio di quando lo stage ha senso. Non sono d'accordo ==per le stringhe però.
Alexander Pogrebnyak,

1
Da Java 7 in poi il "String pool" è implementato nello spazio heap, quindi ottiene tutti i vantaggi della memorizzazione degli stagisti, della garbage collection e la sua dimensione non è limitata, può essere aumentata fino alla dimensione heap. (Non avrai mai bisogno di così tanto memoria per archi)
Anil Uttani,

15

Voglio aggiungere i miei 2 centesimi all'utilizzo ==con stringhe internate.

La prima cosa che String.equalsfa è this==object.

Quindi, anche se c'è un piccolo miglioramento delle prestazioni (non stai chiamando un metodo), dal punto di vista del manutentore usare ==è un incubo, perché alcune stringhe internate hanno la tendenza a diventare non internate.

Quindi suggerisco di non fare affidamento sul caso speciale di ==stringhe internate, ma usare sempre equalscome previsto da Gosling.

EDIT: internato diventando non internato:

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 ) )
     {
        ...
     }
  }
}

Nella versione 2.0 il manutentore ha deciso di rendere hasReferenceValpubblico, senza entrare nei dettagli, che si aspetta una matrice di stringhe internate.

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 ) )
     {
        ...
     }
  }
}

Ora hai un bug, che può essere molto difficile da trovare, perché nella maggior parte dei casi l'array contiene valori letterali e talvolta viene utilizzata una stringa non letterale. Se equalsfossero stati usati invece di ==allora hasReferenceValavrebbero continuato a funzionare. Ancora una volta, il miglioramento delle prestazioni è minimo, ma i costi di manutenzione sono elevati.


"alcune stringhe internate hanno la tendenza a diventare non internate". wow, sarebbe ... strano. Puoi citare un riferimento, per favore?
Carl Smotricz,

2
OK, pensavo che ti stavi riferendo a Strings che stava vagando fuori dalla piscina interna e sul mucchio grazie alla magia nella JVM. Quello che stai dicendo è che == rende più probabili alcune classi di errori del programmatore.
Carl Smotricz,

"Quindi suggerisco di non fare affidamento sul caso speciale di == per le stringhe internate, ma uso sempre uguale a come previsto da Gosling." Hai una citazione diretta o un commento di Gosling che lo afferma? In tal caso, perché si è anche preso la briga di mettere intern () e l'uso di == nella lingua?

1
intern non è buono per il confronto diretto (==), anche se funziona se entrambe le stringhe sono internate. è ottimo ridurre la memoria totale utilizzata: quando la stessa stringa viene utilizzata in più di 1 posizione.
tgkprog,

12

I valori letterali e le costanti di stringa sono internati per impostazione predefinita. Cioè, "foo" == "foo"(dichiarato dai letterali String), ma new String("foo") != new String("foo").


4
Quindi, la domanda è quando dovremmo usare intern,
Rakesh Juyal,

che è stato indicato su stackoverflow.com/questions/1833581/when-to-use-intern e su una serie di altre domande, alcune delle quali di ieri.
Bozho,

Fammi sapere se la mia comprensione per questa affermazione String literals and constants are interned by default:, è corretta. new String("foo")-> Qui, viene creato un "foo" letterale in String nel pool di stringhe e uno in heap, quindi vengono creati 2 oggetti in totale.
dkb,

8

Impara Java String Intern - una volta per tutte

Le stringhe in Java sono oggetti immutabili in base alla progettazione. Pertanto, due oggetti stringa anche con lo stesso valore saranno oggetti diversi per impostazione predefinita. Tuttavia, se desideriamo risparmiare memoria, potremmo indicare di utilizzare la stessa memoria con un concetto chiamato string intern.

Le seguenti regole ti aiuteranno a comprendere il concetto in termini chiari:

  1. La classe String mantiene un pool interno inizialmente vuoto. Questo pool deve garantire di contenere oggetti stringa con solo valori univoci.
  2. Tutti i valori letterali di stringa con lo stesso valore devono essere considerati lo stesso oggetto posizione di memoria perché altrimenti non hanno alcuna nozione di distinzione. Pertanto, tutti questi letterali con lo stesso valore comporteranno un'unica voce nel pool interno e faranno riferimento alla stessa posizione di memoria.
  3. Anche la concatenazione di due o più letterali è letterale. (Pertanto la regola n. 2 sarà applicabile per loro)
  4. Ogni stringa creata come oggetto (cioè con qualsiasi altro metodo tranne quello letterale) avrà posizioni di memoria diverse e non inserirà alcuna voce nel pool interno
  5. La concatenazione di letterali con non letterali renderà non letterale. Pertanto, l'oggetto risultante avrà una nuova posizione di memoria e NON farà una voce nel pool interno.
  6. Richiamando il metodo intern su un oggetto stringa, crea un nuovo oggetto che entra nel pool interno o restituisce un oggetto esistente dal pool con lo stesso valore. L'invocazione su qualsiasi oggetto che non si trova nel pool interno, NON sposta l'oggetto nel pool. Crea piuttosto un altro oggetto che entra nel pool.

Esempio:

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

Nota: i casi motivazionali per lo stagista di stringhe non sono discussi qui. Tuttavia, il risparmio di memoria sarà sicuramente uno degli obiettivi primari.


Grazie per il n. 3, non lo sapevo :)
Kaj

4

dovresti distinguere due periodi di tempo che sono il tempo di compilazione e il tempo di runtime.Ad esempio:

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

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

da un lato, nell'esempio 1, troviamo che i risultati sono tutti restituiti veri, perché nel tempo di compilazione, il jvm inserirà il "test" nel pool di stringhe letterali, se esiste il jvm "test", allora utilizzerà quello esistente, nell'esempio 1, le stringhe "test" puntano tutte allo stesso indirizzo di memoria, quindi l'esempio 1 restituirà true. d'altra parte, nell'esempio 2, il metodo di sottostringa () viene eseguito nel tempo di runtime, nel caso di "test" == "! test" .substring (1), il pool creerà un oggetto a due stringhe ", test "e"! test ", quindi sono diversi oggetti di riferimento, quindi questo caso restituirà false, nel caso di" test "=="! test ".substring (1) .intern (), il metodo di intern ( ) inserirà ""! test ".substring (1)" nel pool di stringhe letterali,


3

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

lo string interning è un metodo per memorizzare solo una copia di ogni valore di stringa distinto, che deve essere immutabile. L'interning delle stringhe rende alcune attività di elaborazione delle stringhe più efficienti in termini di tempo o spazio a costo di richiedere più tempo quando la stringa viene creata o internata. I valori distinti sono memorizzati in un pool interno di stringhe.


2

Le stringhe internate evitano stringhe duplicate. Il interning consente di risparmiare RAM a spese di più tempo CPU per rilevare e sostituire stringhe duplicate. Esiste solo una copia di ogni stringa che è stata internata, indipendentemente da quanti riferimenti la indicano. Poiché le stringhe sono immutabili, se due metodi diversi utilizzano accidentalmente la stessa stringa, possono condividere una copia della stessa stringa. Il processo di conversione di stringhe duplicate in stringhe condivise si chiama interning.String.intern () fornisce l'indirizzo del master canonico String. Puoi confrontare le stringhe internate con simple == (che confronta i puntatori) invece che uguale ache confronta i caratteri della stringa uno per uno. Poiché le stringhe sono immutabili, il processo interno è libero di risparmiare ulteriore spazio, ad esempio, non creando un letterale String separato per "pot" quando esiste come sottostringa di altri letterali come "ippopotamo".

Per saperne di più http://mindprod.com/jgloss/interned.html


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

PRODUZIONE

s1 and s2 are same
s1 and s4 are same

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

Quando due stringhe vengono create in modo indipendente, intern()ti consente di confrontarle e ti aiuta anche a creare un riferimento nel pool di stringhe se il riferimento non esisteva prima.

Quando si utilizza String s = new String(hi), java crea una nuova istanza della stringa, ma quando si utilizza String s = "hi", java verifica se nel codice è presente un'istanza della parola "hi" e se esiste, restituisce semplicemente il riferimento.

Poiché il confronto delle stringhe si basa sul riferimento, ti intern()aiuta a creare un riferimento e ti consente di confrontare il contenuto delle stringhe.

Quando si utilizza intern()nel codice, cancella lo spazio utilizzato dalla stringa facendo riferimento allo stesso oggetto e restituisce semplicemente il riferimento dello stesso oggetto già esistente in memoria.

Ma in caso di p5 quando si utilizza:

String p5 = new String(p3);

Vengono copiati solo i contenuti di p3 e la nuova creazione di p5. Quindi non è internato .

Quindi l'output sarà:

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

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.
}

1

Il metodo string intern () viene utilizzato per creare una copia esatta dell'oggetto stringa heap nel pool di costanti stringa. Gli oggetti stringa nel pool di costanti stringa vengono internati automaticamente, ma non gli oggetti stringa nell'heap. L'uso principale della creazione di stagisti è quello di salvare lo spazio di memoria e di eseguire un confronto più rapido degli oggetti stringa.

Fonte: Cos'è lo string intern in Java?


1

Come hai detto, quel intern()metodo stringa troverà prima dal pool String, se lo trova, quindi restituirà l'oggetto che punta a quello o aggiungerà una nuova stringa nel pool.

    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

I s1e s2sono due oggetti che puntano al pool di stringhe "Hello", e usando "Hello".intern()troveranno quello s1e s2. Quindi "s1 == s3"ritorna vero, così come al s3.intern().


Questo in realtà non fornisce molte nuove informazioni. C'è già una risposta esclusa.
Alexander,

0

Usando il riferimento all'oggetto heap se vogliamo ottenere il riferimento all'oggetto del pool di costanti stringa corrispondente , dovremmo andare per intern ()

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

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

Vista pittorica inserisci qui la descrizione dell'immagine

Passaggio 1: l' oggetto con i dati 'Rakesh' viene creato nel pool di costanti di heap e stringhe. Inoltre s1 punta sempre verso l'oggetto heap.

Step 2: Usando l'oggetto heap di riferimento s1, stiamo cercando di ottenere il riferimento costante di stringa corrispondente all'oggetto pool sencenc s2, usando intern ()

Passaggio 3: Creazione intenzionale di un oggetto con dati "Rakesh" nel pool di costanti di stringhe, a cui fa riferimento il nome s3

Come operatore "==" inteso per confronto di riferimento.

Diventare falso per s1 == s2

Diventare realtà per s2 == s3

Spero che questo aiuto !!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.