CodeGym /Blog Java /Aleatoriu /Comparați comparații String și Equals în Java
John Squirrels
Nivel
San Francisco

Comparați comparații String și Equals în Java

Publicat în grup
Bună! Astăzi vom vorbi despre un subiect foarte important și interesant, și anume, compararea obiectelor cu obiecte (Compare Strings and Equals). Deci, în Java, când anume ar fi obiectul A egal cu obiectul B ? Să încercăm să scriem un exemplu:

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);
   }
}
Ieșire consolă: fals Așteptați, opriți. De ce aceste două mașini nu sunt egale? Le-am atribuit aceleași proprietăți, dar rezultatul comparației este fals. Răspunsul este simplu. Operatorul == compară referințele obiectelor, nu proprietățile obiectului. Două obiecte ar putea avea chiar și 500 de câmpuri cu valori identice, dar compararea lor ar fi încă fals. La urma urmei, se referă la car1 și car2indică două obiecte diferite, adică două adrese diferite. Imaginați-vă o situație în care comparați oameni. Cu siguranță, undeva în lume există o persoană care vă împărtășește același nume, culoarea ochilor, vârsta, înălțimea, culoarea părului etc. Asta vă face să vă asemănați în multe privințe, dar încă nu sunteți gemeni - și, evident, nu sunteți aceeasi persoana.
Egalități și comparații de șiruri - 2
Operatorul == folosește aproximativ aceeași logică atunci când îl folosim pentru a compara două obiecte. Dar ce se întâmplă dacă aveți nevoie ca programul dvs. să folosească o logică diferită? De exemplu, să presupunem că programul dvs. efectuează analize ADN. Acesta compară codul genetic a doi oameni și determină dacă aceștia sunt gemeni.

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);
   }
}
Ieșire din consolă: false Obținem același rezultat logic (pentru că nu ne-am schimbat prea mult), dar acum acea logică nu este bună! La urma urmei, în viața reală, analiza ADN-ului ar trebui să ne ofere o garanție de 100% că avem gemeni în fața noastră. Dar programul nostru și operatorul == ne spun contrariul. Cum schimbăm acest comportament și ne asigurăm că programul dă rezultatul corect atunci când ADN-ul se potrivește? Java are o metodă specială pentru aceasta: equals() . La fel ca metoda toString() , despre care am discutat mai devreme, equals() aparține clasei Object — cea mai importantă clasă din Java, clasa din care derivă toate celelalte clase. Dar este egal()nu schimbă comportamentul programului nostru de la sine:

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));
   }
}
Ieșire din consolă: false Exact același rezultat, deci pentru ce avem nevoie de această metodă? :/ Totul este simplu. Problema aici este că în prezent folosim această metodă, deoarece este implementată în clasa Object . Și dacă intrăm în codul clasei Object și ne uităm la implementarea metodei, iată ce vom vedea:

public boolean equals(Object obj) {
   return (this == obj);
}
Acesta este motivul pentru care comportamentul programului nu s-a schimbat! Același operator == (care compară referințele) este folosit în cadrul metodei equals() a clasei Object . Dar trucul cu această metodă este că o putem suprascrie. A suprascrie înseamnă a-ți scrie propria metodă equals() în clasa noastră Man , oferindu-i comportamentul de care avem nevoie! În prezent, nu ne place faptul că man1.equals(man2) este în esență echivalent cu man1 == man2 . Iată ce vom face în această situație:

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

   } 
} 
Ieșire din consolă: adevărat Acum obținem un rezultat complet diferit! Scriind propria noastră metodă equals() și utilizând-o în loc de cea standard, am produs comportamentul corect: acum, dacă doi oameni au același ADN, programul raportează „Analiza ADN a dovedit că sunt gemeni” și returnează adevărat! Prin suprascrierea metodei equals() din clasele dvs., puteți crea cu ușurință orice logică de comparare a obiectelor de care aveți nevoie. De fapt, tocmai am atins compararea obiectelor. În fața noastră, există încă o mare lecție de sine stătătoare pe această temă (o treceți peste ea acum dacă sunteți interesat).

Compararea șirurilor de caractere în Java

De ce luăm în considerare comparațiile de șiruri separat de orice altceva? Realitatea este că șirurile sunt un subiect în sine în programare. În primul rând, dacă luați toate programele Java scrise vreodată, veți descoperi că aproximativ 25% dintre obiectele din ele sunt șiruri. Deci acest subiect este foarte important. În al doilea rând, procesul de comparare a șirurilor este într-adevăr foarte diferit de alte obiecte. Luați în considerare un exemplu simplu:

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);
   }
}
Ieșire din consolă: false Dar de ce am primit false? La urma urmei, șirurile sunt exact aceleași, cuvânt cu cuvânt :/ S-ar putea să fi ghicit motivul: este pentru că operatorul == compară referințele ! În mod clar, s1 și s2 au adrese diferite în memorie. Dacă te-ai gândit la asta, atunci hai să reluăm exemplul nostru:

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);
   }
}
Acum avem din nou două referințe, dar rezultatul este exact opus: Ieșire din consolă: adevărat Neputincios confuz? Să ne dăm seama ce se întâmplă. Operatorul == chiar compară adresele de memorie. Acest lucru este întotdeauna adevărat și nu trebuie să te îndoiești de asta. Asta înseamnă că dacă s1 == s2 returnează adevărat, atunci aceste două șiruri au aceeași adresă. Și într-adevăr acest lucru este adevărat! Este timpul să vă prezentăm o zonă specială de memorie pentru stocarea șirurilor de caractere: pool-ul de șiruri
Egalități și comparații de șiruri - 3
Pool-ul de șiruri este o zonă pentru stocarea tuturor valorilor șirurilor pe care le creați în programul dvs. De ce a fost creat? După cum am spus mai înainte, șirurile reprezintă un procent uriaș din toate obiectele. Orice program mare creează o mulțime de șiruri. Pool-ul de șiruri a fost creat pentru a economisi memorie: șirurile sunt plasate acolo și apoi șirurile create ulterior se referă la aceeași zonă de memorie - nu este nevoie să alocați memorie suplimentară de fiecare dată. De fiecare dată când scrieți String = "........", programul verifică dacă există un șir identic în pool-ul de șiruri. Dacă există, atunci un șir nou nu va fi creat. Și noua referință va indica aceeași adresă în pool-ul de șiruri (unde se află șirul identic). Deci când am scris

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2 indică același loc cu s1 . Prima instrucțiune creează un șir nou în pool-ul de șiruri. A doua afirmație se referă pur și simplu la aceeași zonă de memorie ca s1 . Ai putea face alte 500 de șiruri identice și rezultatul nu s-ar schimba. Așteptaţi un minut. Dacă este adevărat, atunci de ce nu a funcționat acest exemplu înainte?

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);
   }
}
Cred că intuiția ți-a spus deja motivul =) Încercați să ghiciți înainte de a citi mai departe. Puteți vedea că aceste două șiruri au fost declarate în moduri diferite. Unul cu noul operator, iar celălalt fără acesta. Aici este motivul. Când noul operator este folosit pentru a crea un obiect, acesta alocă forțat o nouă zonă de memorie pentru obiect. Și un șir creat folosind new nu ajunge în pool-ul de șiruri - devine un obiect separat, chiar dacă textul său se potrivește perfect cu un șir din grupul de șiruri. Adică dacă scriem următorul cod:

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!");
   }
}
În memorie, arată astfel:
Egalități și comparații de șiruri - 4
Și de fiecare dată când creați un obiect nou folosind new , o nouă zonă de memorie este alocată, chiar dacă textul din noul șir este același! Se pare că ne-am dat seama de operatorul == . Dar cum rămâne cu noua noastră cunoștință, metoda 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));
   }
}
Ieșire din consolă: adevărat Interesant. Suntem siguri că s1 și s2 indică diferite zone din memorie. Dar metoda equals() încă ne spune că sunt egali. De ce? Amintiți-vă că am spus anterior că metoda equals() poate fi suprascrisă pentru a compara obiectele așa cum dorim? Tocmai asta au făcut cu clasa String . Ea suprascrie equals()metodă. Și în loc să compare referințele, compară secvența de caractere din șiruri. Dacă textul este același, atunci nu contează cum au fost create sau unde sunt stocate: fie în pool-ul de șiruri sau într-o zonă separată de memorie. Rezultatul comparației va fi adevărat. Apropo, Java vă permite să efectuați comparații de șiruri fără majuscule. În mod normal, dacă unul dintre șiruri are toate literele mari, atunci rezultatul comparației va fi fals:

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));
   }
}
Ieșire din consolă: false Pentru comparațiile care nu țin cont de majuscule, clasa String are metoda equalsIgnoreCase() . Îl puteți folosi dacă vă interesează doar să comparați secvența de caractere specifice, mai degrabă decât majusculele. De exemplu, acest lucru ar putea fi util când comparăm două adrese:

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));
   }
}
În acest caz, vorbim evident despre aceeași adresă, așa că are sens să folosim metoda equalsIgnoreCase() .

Metoda String.intern().

Clasa String are încă o metodă complicată: intern() ; Metoda intern() funcționează direct cu pool-ul de șiruri. Dacă apelați metoda intern() pe un șir de caractere:
  • Verifică dacă există un șir care se potrivește în pool-ul de șiruri
  • Dacă există, returnează referința la șirul din pool
  • Dacă nu, adaugă șirul la pool-ul de șiruri și returnează o referință la acesta.
După folosirea metodei intern() pe o referință de șir obținută folosind new , putem folosi operatorul == pentru a o compara cu o referință de șir din pool-ul de șiruri.

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());
   }
}
Ieșire din consolă: true Când am comparat anterior aceste șiruri fără intern() , rezultatul a fost fals. Acum metoda intern() verifică dacă șirul „CodeGym este cel mai bun site pentru a învăța Java!” este în pool-ul de corzi. Desigur, este: noi l-am creat cu

String s1 = "CodeGym is the best website for learning Java!";
Verificăm dacă s1 și referința returnate de s2.intern() indică aceeași zonă de memorie. Și, desigur, o fac :) În rezumat, memorează și aplică această regulă importantă: folosește ÎNTOTDEAUNA metoda equals() pentru a compara șirurile! Când comparăm șiruri, aproape întotdeauna ne referim să le comparăm caracterele, mai degrabă decât referințele, zonele de memorie sau orice altceva. Metoda equals() face exact ceea ce aveți nevoie. Pentru a consolida ceea ce ați învățat, vă sugerăm să urmăriți o lecție video de la Cursul nostru Java

Mai multe lecturi:

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