Stringhe in Java: uguale a ==

Possibile duplicato:
Come faccio a confrontare le stringhe in Java?

String s1 = "andrei"; String s2 = "andrei"; String s3 = s2.toString(); System.out.println((s1==s2) + " " + (s2==s3)); 

Dando il seguente codice, perché il secondo confronto s2 == s3 è vero? Cosa restituisce in realtà s2.toString ()? Dove si trova effettivamente (s2.toString()) ?

Prima di tutto String.toString è un no-op:

 /** * This object (which is already a string!) is itself returned. * * @return the string itself. */ public String toString() { return this; } 

In secondo luogo, le costanti String sono internate in modo che s1 e s2 siano dietro le quinte modificate per essere la stessa istanza String.

Il metodo String.intern () può essere utilizzato per garantire che stringhe uguali abbiano riferimenti uguali. Le costanti di stringa sono intern , quindi s1 e s2 faranno riferimento alla stessa stringa. String.toString() restituisce semplicemente se stesso, ovvero a.toString() restituisce a, quando a è una stringa. Quindi, s2 anche == s3.

In generale, le stringhe non devono essere confrontate con l’uguaglianza di riferimento, ma con l’uguaglianza di valore, usando equals() . Il motivo è che è facile ottenere due stringhe equivalenti ma riferimenti diversi. Ad esempio, quando si creano sottostringhe. Un’eccezione a questa regola è che se si conoscono entrambe le stringhe sono state intern preparate (o internate come parte del confronto).

Per rispondere alla domanda implicita sull’heap o sullo stack, le stringhe vengono allocate nell’heap. Anche se sono stati allocati nello stack, ad esempio con l’imminente analisi di escape e l’allocazione dello stack, la semantica del programma non cambierà e otterrete lo stesso risultato per l’allocazione di heap e stack.

Fondamentalmente quando si utilizza la new String("something") si sta costringendo Java a creare un object nuovo di zecca.

Quando si assegna a un valore String letterale ="something" , tale stringa viene archiviata nel pool costante, un’ottimizzazione eseguita dalla JVM. Pertanto, quando assegni un altro riferimento alla stessa costante, l’object memorizzato nel pool costante viene riutilizzato, in pratica si tratta dello stesso object.

Dovresti usare .equals e non == quando confronti le stringhe java.

== Confronta i riferimenti e quindi s2.ToString () restituisce s2.

Dal glossario java su heap / stack:

 In Sun's JVM, the interned Strings (which includes String literals) are 

memorizzato in un pool speciale di RAM chiamato perm gen, in cui la JVM carica anche classi e memorizza codice compilato in modo nativo. Tuttavia, le stringhe interposte non si comportano diversamente da come erano state memorizzate nell’heap di oggetti ordinario.

Dalla specifica della macchina virtuale java:

I valori letterali stringa e, più in generale, le stringhe che rappresentano i valori delle espressioni costanti sono “internati” in modo da condividere istanze univoche, utilizzando il metodo String.intern.

"andrei" è un letterale String e quindi “internato”. Pertanto, sia String s1 che s2 riferiscono allo stesso object String, una stringa internata con il contenuto "andrei" .

Pertanto (s1 == s2) && (s1.equals(s2)) è true . String#toString() non crea una nuova String (come molti altri metodi da String ) ma restituisce semplicemente this . Quindi s2 == s3 è true .

Perché il primo risultato è falso?

== confronta i riferimenti e crei due oggetti diversi.

Ho capito che per nessuno dei tipi primitivi, quando facciamo ‘==’,

Una stringa non è un primitivo. I riferimenti saranno == quando si riferiscono allo stesso object.

Dato che == confronta i riferimenti, puoi vedere che s2 == s3 è vero che s2.toString() restituisce s2.

Poiché String è immutable, un’implementazione utile del metodo toString è, nella class String, la restituzione.

Ad esempio, il mio rt.jar contiene la seguente implementazione:

 public String toString() { return this; } 

Di conseguenza, il riferimento associato a s3 è uguale a quello associato a s2 .

Considerando l’istruzione s1==s2 , è dovuto alla chiamata automatica a intern() per tutte le stringhe costanti. Ciò significa che al momento della compilazione, il codice di inizializzazione s2 verrà sostituito da s2=s1 , il che rende l’asserzione abbastanza ovvia, no?

Solo le stringhe internate ( String.intern() ) sono sicure da confrontare con == per tutti gli altri casi che è necessario utilizzare equals .

Nel tuo caso tu definisci il tuo tipo MyString che ha un piccolo in comune con java String quindi confrontando s e s1 confronti i riferimenti a due oggetti distinti, ecco perché sei diventato false .

Ogni istanza di MyString trova in una diversa posizione di memoria, quindi, dimenticando il contenuto delle istanze, per ogni due diverse istanze il test == sta per risultare false .

Nel caso della class String , c’è una piccola ma importante differenza quando si assegna una variabile String e l’operatore sul lato destro è letterale (cioè String s = "foo"; ), una nuova posizione di memoria sta per essere occupato da “foo” solo se “foo” non è stato incontrato prima come letterale. Se questo è il caso (cioè String s = "foo"; String otherS = "foo"; ), otherS si otherS a fare riferimento al “foo” già presente.

Questo comportamento è chiamato pooling di stringhe .

Confrontando s == s1 stai confrontando due diversi oggetti MyString . Pensa alla tua situazione in questo modo

inserisci la descrizione dell'immagine qui

s e s1 sono oggetti diversi, ma il loro attributo punta alla stessa istanza di toto .

Sono oggetti diversi a causa del modo in cui li crei:

 MyString s = new MyString("toto"); MyString s1 = new MyString("toto"); 

Quindi s == s1 restituirà false . Perché restituisca true , dovevano essere lo stesso object. Potresti ottenerlo in questo modo:

 MyString s = new MyString("toto"); MyString s1 = s; 

Questo sarebbe il risultato

inserisci la descrizione dell'immagine qui

La ragione per cui il primo confronto fallisce è che tu crei due oggetti chiamando new . Ora, l’operatore == confronta due indirizzi di memoria , che restituiscono il rendimento ottenuto perché i due oggetti non si trovano nella stessa cella di memoria.

Il motivo per cui funziona con stringhe costanti è che il compilatore java, javac , ottimizza il codice. Con questa ottimizzazione costanti di stringa simili vengono collocate in una stessa cella di memoria . Se avessi fatto quanto segue, il risultato sarebbe stato lo stesso per gli oggetti String .

 String s2 = new String("toto"); String s3 = new String("toto"); System.out.println(s2==s3); //yields false!! 

La tua strada da percorrere è .equals (altro). Per questo dovrai implementare il metodo uguale nella class Mystring:

 class MyString{ private String s; public MyString (String s){ this.s = s; } public String getContent(){ return s; } @Override public boolean equals(Object other){ if(other instanceof MyString){ MyString compareTo = (MyString) other; return this.s.equals(compareTo.getContent()); } return false; } } 

“==” è un riferimento di confronto. Ma il metodo Equals () nella class Object è solo di riferimento. E il comportamento di method equals () nella sottoclass si basa sull’implementazione di override di questo metodo.

Quando si utilizza == sulle stringhe, vengono confrontati solo i riferimenti. Pertanto, == è garantito per restituire true in situazioni come:

 String s1 = "..."; String s2 = s1; // reference assignment! 

Qui, s1 == s2 . In tutte le altre situazioni, == può o non può restituire true anche se le due stringhe contengono la stessa sequenza di caratteri.

Per confrontare il contenuto delle due stringhe, usa equals() :

 if (s1.equals(s2)) { ... } 

s2.toString () restituisce una rappresentazione String. Poiché è già una stringa, restituisce sé stesso (ecco perché il confronto è vero).

Tutte le stringhe sono allocate sull’heap, l’operatore di confronto confronta solo se sono lo stesso object (ecco perché s1! = S2).