2013-01-23 18 views
10

La questione si riduce a questo codice:Interning stringa Java, cosa è garantito?

// setup 
String str1 = "some string"; 
String str2 = new String(str1); 
assert str1.equals(str2); 
assert str1 != str2; 
String str3 = str2.intern(); 

// question cases 
boolean case1 = str1 == "some string"; 
boolean case2 = str1 == str3; 

non standard di Java fornisce alcuna garanzia sui valori di case1 e case2? Il collegamento alla parte rilevante delle specifiche Java sarebbe bello, ovviamente.

Sì, ho esaminato tutte le "Domande simili" trovate da SO, e non ho trovato duplicati, poiché nessuno di quelli che ho trovato ha risposto alla domanda in questo modo. E no, non si tratta dell'idea sbagliata di "ottimizzare" i confronti tra stringhe sostituendo equals con ==.

+2

Se non si tratta di un tentativo di ottimizzazione inutile, che cos'è? –

+0

@dystroy Il caso particolare che ho avuto riguardava l'utilizzo di nomi di file normalizzati per la sincronizzazione e se le stringhe internate sono sicure per lo scopo o se fosse necessario un 'Map fileNameToLockObjectMap' condiviso. Non essendo sicuro, ho finito per usare una mappa in quel caso (e non tornerò a cambiare). – hyde

+0

Il blocco delle stringhe interne non è una buona idea. È molto probabile che tu abbia conseguenze non volute. –

risposta

13

Ecco il tuo preventivo JLS, Section 3.10.5:

Ogni stringa letterale è un riferimento (§4.3) a un'istanza (§4.3.1, §12.5 ) della classe String (§4.3.3) . Gli oggetti stringa hanno un valore costante. Stringhe letterali o, più in generale, stringhe che rappresentano i valori delle espressioni costanti (§15.28): sono "internate" in modo da condividere istanze uniche , utilizzando il metodo String.intern.

Pertanto, il programma di test costituito dall'unità compilazione (§7.3):

package testPackage; 
class Test { 
     public static void main(String[] args) { 
       String hello = "Hello", lo = "lo"; 
       System.out.print((hello == "Hello") + " "); 
       System.out.print((Other.hello == hello) + " "); 
       System.out.print((other.Other.hello == hello) + " "); 
       System.out.print((hello == ("Hel"+"lo")) + " "); 
       System.out.print((hello == ("Hel"+lo)) + " "); 
       System.out.println(hello == ("Hel"+lo).intern()); 
     } 
} 

class Other { static String hello = "Hello"; } 

e l'unità di compilazione:

package other; 

public class Other { static String hello = "Hello"; } 

produce l'uscita: vero vero vero vero falso vero

Questo esempio illustra sei punti:

stringhe letterali all'interno della stessa classe (§ 8) nello stesso pacchetto (§ 7) rappresentano riferimenti allo stesso oggetto String (§4.3.1).

Le stringhe letterali all'interno di classi diverse nello stesso pacchetto rappresentano i riferimenti allo stesso oggetto String.

Le stringhe letterali all'interno di classi diverse in diversi pacchetti rappresentano allo stesso modo riferimenti allo stesso oggetto String.

Le stringhe calcolate da espressioni costanti (§15.28) sono calcolate al momento della compilazione e quindi trattate come se fossero letterali.

Le stringhe calcolate mediante concatenazione in fase di runtime sono state appena create e quindi distinte. Il risultato di internare esplicitamente una stringa calcolata è la stessa stringa di qualsiasi stringa letterale preesistente con lo stesso contenuto dello .

In combinazione con la JavaDoc per tirocinante, e si dispone di informazioni sufficienti per dedurre che entrambi i vostri casi restituirà true.

+0

Come funziona nell'ambiente multithread, ad es. quando intern() è calpestato più volte da più thread contemporaneamente. Immagino che questo sia curato, ma è solo una supposizione. Ne sai di più? – Alpedar

+2

@Alpedar - l'implementazione di intern è nativa e dipende dalla piattaforma. Presumo che sia thread-safe basato su JavaDoc, ma la loro non è una garanzia (che ho trovato) nella documentazione ufficiale che supporta questo. – Perception

5

Penso API String.Intern fornisce informazioni sufficienti

Un pool di stringhe, inizialmente vuota, viene mantenuta privata della classe String.

Quando viene richiamato il metodo interno, se il pool contiene già una stringa uguale a questo oggetto String come determinato dal metodo equals (Object), viene restituita la stringa dal pool. Altrimenti, questo oggetto String viene aggiunto al pool e viene restituito un riferimento a questo oggetto String.

Segue che per ogni due stringhe s e t, s.intern() == t.intern() è vera se e solo se s.equals (t) è vera.

Tutte le stringhe letterali e le espressioni costanti con valori stringa sono internate. I valori letterali stringa sono definiti nella sezione 3.10.5 di The Java ™ Language Specification.