CodeGym /Java Blog /Random-IT /Confronta i confronti String e Equals in Java
John Squirrels
Livello 41
San Francisco

Confronta i confronti String e Equals in Java

Pubblicato nel gruppo Random-IT
CIAO! Oggi parleremo di un argomento molto importante e interessante, ovvero il confronto di oggetti con oggetti (Confronta stringhe ed uguali). Quindi in Java, quando esattamente l'oggetto A sarebbe uguale all'oggetto B ? Proviamo a scrivere un esempio:

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {
      
       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
Output della console: falso Attendi, fermati. Perché queste due auto non sono uguali? Abbiamo assegnato loro le stesse proprietà, ma il risultato del confronto è falso. La risposta è semplice. L' operatore == confronta i riferimenti agli oggetti, non le proprietà degli oggetti. Due oggetti potrebbero anche avere 500 campi con valori identici, ma il loro confronto produrrebbe comunque false. Dopotutto, fa riferimento a car1 e car2puntare a due oggetti diversi, cioè a due indirizzi diversi. Immagina una situazione in cui stai confrontando le persone. Certamente, da qualche parte nel mondo c'è una persona che condivide il tuo stesso nome, colore degli occhi, età, altezza, colore dei capelli, ecc. la stessa persona.
Uguali e confronti di stringhe - 2
L' operatore == usa approssimativamente questa stessa logica quando lo usiamo per confrontare due oggetti. Ma cosa succede se hai bisogno che il tuo programma utilizzi una logica diversa? Ad esempio, supponiamo che il tuo programma esegua l'analisi del DNA. Confronta il codice genetico di due persone e determina se sono gemelli.

public class Man {

   int geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = 1111222233;

       Man man2 = new Man();
       man2.geneticCode = 1111222233;

       System.out.println(man1 == man2);
   }
}
Console output: false Otteniamo lo stesso risultato logico (perché non abbiamo cambiato molto), ma ora quella logica non va bene! Dopotutto, nella vita reale, l'analisi del DNA dovrebbe darci una garanzia al 100% che abbiamo dei gemelli davanti a noi. Ma il nostro programma e l' operatore == ci dicono il contrario. Come modifichiamo questo comportamento e ci assicuriamo che il programma fornisca il risultato corretto quando il DNA corrisponde? Java ha un metodo speciale per questo: equals() . Come il metodo toString() , di cui abbiamo discusso in precedenza, equals() appartiene alla classe Object , la classe più importante in Java, la classe da cui derivano tutte le altre classi. Ma uguale()non cambia da solo il comportamento del nostro programma:

public class Man {

   String geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = "111122223333";

       Man man2 = new Man();
       man2.geneticCode = "111122223333";

       System.out.println(man1.equals(man2));
   }
}
Output della console: false Esattamente lo stesso risultato, quindi a cosa ci serve questo metodo? :/ È tutto semplice. Il problema qui è che attualmente stiamo usando questo metodo poiché è implementato nella classe Object . E se entriamo nel codice della classe Object e osserviamo l'implementazione del metodo, questo è ciò che vedremo:

public boolean equals(Object obj) {
   return (this == obj);
}
Questo è il motivo per cui il comportamento del programma non è cambiato! Lo stesso operatore == (che confronta i riferimenti) viene utilizzato all'interno del metodo equals() della classe Object . Ma il trucco con questo metodo è che possiamo ignorarlo. Eseguire l'override significa scrivere il proprio metodo equals() nella nostra classe Man , dandogli il comportamento di cui abbiamo bisogno! Al momento, non ci piace il fatto che man1.equals(man2) sia essenzialmente equivalente a man1 == man2 . Ecco cosa faremo in questa situazione:

public class Man { 

   int dnaCode; 

   public boolean equals(Man man) { 
       return this.dnaCode ==  man.dnaCode; 

   } 

   public static void main(String[] args) { 

       Man man1 = new Man(); 
       man1.dnaCode = 1111222233; 

       Man man2 = new Man(); 
       man2.dnaCode = 1111222233; 

       System.out.println(man1.equals(man2)); 

   } 
} 
Output della console: true Ora otteniamo un risultato completamente diverso! Scrivendo il nostro metodo equals() e usandolo al posto di quello standard, abbiamo prodotto il comportamento corretto: ora se due persone hanno lo stesso DNA, il programma riporta "l'analisi del DNA ha dimostrato che sono gemelli" e restituisce true! Sovrascrivendo il metodo equals() nelle tue classi, puoi facilmente creare qualsiasi logica di confronto degli oggetti di cui hai bisogno. In effetti, abbiamo appena accennato al confronto tra oggetti. Davanti a noi, c'è ancora una grande lezione a sé stante su questo argomento (scorrila ora se sei interessato).

Confronto di stringhe in Java

Perché stiamo considerando i confronti tra stringhe separatamente da tutto il resto? La realtà è che le stringhe sono un argomento a sé stante nella programmazione. Innanzitutto, se prendi tutti i programmi Java mai scritti, scoprirai che circa il 25% degli oggetti in essi contenuti sono stringhe. Quindi questo argomento è molto importante. In secondo luogo, il processo di confronto delle stringhe è davvero molto diverso da quello di altri oggetti. Considera un semplice esempio:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
Output della console: false Ma perché abbiamo ottenuto false? Dopotutto, le stringhe sono esattamente le stesse, parola per parola :/ Potresti aver indovinato il motivo: è perché l' operatore == confronta i riferimenti ! Chiaramente, s1 e s2 hanno indirizzi diversi in memoria. Se ci hai pensato, rielaboriamo il nostro esempio:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       System.out.println(s1 == s2);
   }
}
Ora abbiamo di nuovo due riferimenti, ma il risultato è esattamente l'opposto: Uscita della console: vero Impotentemente confuso? Scopriamo cosa sta succedendo. L' operatore == confronta davvero gli indirizzi di memoria. Questo è sempre vero e non c'è bisogno di dubitarne. Ciò significa che se s1 == s2 restituisce true, allora queste due stringhe hanno lo stesso indirizzo. E infatti questo è vero! È giunto il momento di presentarvi un'area di memoria speciale per l'archiviazione delle stringhe: il pool di stringhe
Uguali e confronti di stringhe - 3
Il pool di stringhe è un'area per memorizzare tutti i valori di stringa che crei nel tuo programma. Perché è stato creato? Come abbiamo detto prima, le stringhe rappresentano una percentuale enorme di tutti gli oggetti. Qualsiasi programma di grandi dimensioni crea molte stringhe. Il pool di stringhe è stato creato per risparmiare memoria: le stringhe vengono posizionate lì e le stringhe create successivamente fanno riferimento alla stessa area di memoria, non è necessario allocare memoria aggiuntiva ogni volta. Ogni volta che scrivi String = "........." il programma controlla se c'è una stringa identica nel pool di stringhe. Se esiste, non verrà creata una nuova stringa. E il nuovo riferimento punterà allo stesso indirizzo nel pool di stringhe (dove si trova la stringa identica). Così quando abbiamo scritto

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2 punta allo stesso posto di s1 . La prima istruzione crea una nuova stringa nel pool di stringhe. La seconda affermazione si riferisce semplicemente alla stessa area di memoria di s1 . Potresti creare altre 500 stringhe identiche e il risultato non cambierebbe. Apetta un minuto. Se è vero, allora perché questo esempio non ha funzionato prima?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
Penso che il tuo intuito ti abbia già detto il motivo =) Prova a indovinare prima di leggere oltre. Puoi vedere che queste due stringhe sono state dichiarate in modi diversi. Uno con il nuovo operatore e l'altro senza di esso. Qui sta il motivo. Quando l' operatore new viene utilizzato per creare un oggetto, alloca forzatamente una nuova area di memoria per l'oggetto. E una stringa creata usando new non finisce nel pool di stringhe: diventa un oggetto separato, anche se il suo testo corrisponde perfettamente a una stringa nel pool di stringhe. Cioè, se scriviamo il seguente codice:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       String s3 = new String("CodeGym is the best website for learning Java!");
   }
}
In memoria, sembra così:
Uguali e confronti di stringhe - 4
E ogni volta che crei un nuovo oggetto usando new , viene allocata una nuova area di memoria, anche se il testo all'interno della nuova stringa è lo stesso! Sembra che abbiamo capito l' operatore == . Ma per quanto riguarda la nostra nuova conoscenza, il metodo equals() ?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1.equals(s2));
   }
}
Uscita console: vero Interessante. Siamo certi che s1 e s2 puntino a diverse aree della memoria. Ma il metodo equals() ci dice ancora che sono uguali. Perché? Ricordi che in precedenza abbiamo detto che il metodo equals() potrebbe essere sovrascritto per confrontare gli oggetti come vogliamo? Questo è proprio quello che hanno fatto con la classe String . Sostituisce gli equals()metodo. E invece di confrontare i riferimenti, confronta la sequenza di caratteri nelle stringhe. Se il testo è lo stesso, non importa come sono stati creati o dove sono archiviati: se nel pool di stringhe o in un'area di memoria separata. Il risultato del confronto sarà vero. A proposito, Java ti consente di eseguire confronti di stringhe senza distinzione tra maiuscole e minuscole. Normalmente, se una delle stringhe contiene tutte lettere maiuscole, il risultato del confronto sarà falso:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CODEGYM IS THE BEST WEBSITE FOR LEARNING JAVA!");
       System.out.println(s1.equals(s2));
   }
}
Output della console: false Per i confronti senza distinzione tra maiuscole e minuscole, la classe String ha il metodo equalsIgnoreCase() . Puoi usarlo se ti interessa solo confrontare la sequenza di caratteri specifici piuttosto che il maiuscolo. Ad esempio, questo potrebbe essere utile quando si confrontano due indirizzi:

public class Main {

   public static void main(String[] args) {

       String address1 = "2311 Broadway Street, San Francisco";
       String address2 = new String("2311 BROADWAY STREET, SAN FRANCISCO");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
In questo caso, stiamo ovviamente parlando dello stesso indirizzo, quindi ha senso usare il metodo equalsIgnoreCase() .

Il metodo String.intern()

La classe String ha un metodo più complicato: intern() ; Il metodo intern() funziona direttamente con il pool di stringhe. Se chiami il metodo intern() su una stringa:
  • Controlla se esiste una stringa corrispondente nel pool di stringhe
  • Se esiste, restituisce il riferimento alla stringa nel pool
  • In caso contrario, aggiunge la stringa al pool di stringhe e restituisce un riferimento ad essa.
Dopo aver usato il metodo intern() su un riferimento a una stringa ottenuto usando new , possiamo usare l' operatore == per confrontarlo con un riferimento a una stringa dal pool di stringhe.

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2.intern());
   }
}
Output della console: true Quando abbiamo confrontato queste stringhe in precedenza senza intern() , il risultato è stato false. Ora il metodo intern() controlla se la stringa "CodeGym è il miglior sito per imparare Java!" è nel pool di stringhe. Certo che lo è: l'abbiamo creato con

String s1 = "CodeGym is the best website for learning Java!";
Verifichiamo se s1 e il riferimento restituito da s2.intern() puntano alla stessa area di memoria. E, naturalmente, lo fanno :) In sintesi, memorizza e applica questa importante regola: usa SEMPRE il metodo equals() per confrontare le stringhe! Quando confrontiamo stringhe, quasi sempre intendiamo confrontare i loro caratteri piuttosto che riferimenti, aree di memoria o qualsiasi altra cosa. Il metodo equals() fa esattamente ciò di cui hai bisogno. Per rafforzare ciò che hai imparato, ti suggeriamo di guardare una lezione video dal nostro corso Java

Altre letture:

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION