1. Sammenligne objekter i Java

I Java kan objekter sammenlignes både etter referanse og etter verdi.

Sammenligning av referanser

Hvis to variabler peker på det samme objektet i minnet, er referansene som er lagret i disse variablene like. Hvis du sammenligner disse variablene ved å bruke likhetsoperatoren ( ==), får du sannhet, og det resultatet gir mening. Alt er enkelt her.

Kode Konsollutgang
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

Sammenligning etter verdi

Men du kan ofte støte på situasjoner der to variabler refererer til to distinkte objekter som er identiske. For eksempel to forskjellige strengobjekter som inneholder samme tekst.

For å finne ut om ulike objekter er identiske, bruk equals()metoden. For eksempel:

Kode Konsollutgang
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

Metoden equalser ikke begrenset til Stringklassen. Hver klasse har det.

Til og med klasser som du skriver på egen hånd, og her er hvorfor.



2. Objectklasse

Alle klasser i Java arver Objectklassen. Javas skapere kom opp med denne tilnærmingen.

Og hvis en klasse arver Objectklassen, får den alle metodene til klassen Object. Og dette er en stor konsekvens av arv.

Med andre ord, hver klasse har metodene til Objectklassen, selv om koden deres ikke nevner dem.

Disse nedarvede metodene inkluderer metoder relatert til objektsammenligning. Dette er equals()og hashCode()metodene.

Kode I virkeligheten, her er det vi har:
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

I eksemplet ovenfor opprettet vi en enkel Personklasse med navn og aldersparametere, men ikke en enkelt metode. Men fordi alle klasser arver Objectklassen, Personhar klassen automatisk to metoder:

Metode Beskrivelse
boolean equals(Object obj)
Sammenligner gjeldende objekt og bestått objekt
int hashCode()
Returnerer hashkoden til gjeldende objekt

Det viser seg at absolutt alle objekter har equalsmetoden, og objekter av forskjellige typer kan sammenlignes med hverandre. Slik kode vil kompilere og fungere perfekt.

Kode Konsollutgang
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()metode

Metoden equals(), arvet fra Objectklassen, implementerer den enkleste algoritmen for å sammenligne gjeldende objekt med beståtte objekter: den sammenligner bare referanser til objektene.

Du får samme resultat hvis du bare sammenligner Personvariabler i stedet for å kalle equals()metoden. Eksempel:

Kode Konsollutgang
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

Når equalsmetoden kalles på a, sammenligner den ganske enkelt referansen som er lagret i avariabelen med referansen som er lagret i bvariabelen.

Sammenligning fungerer imidlertid annerledes for Stringklassen. Hvorfor?

Fordi folkene som opprettet Stringklassen skrev sin egen implementering av equals()metoden.

Implementering av equals()metoden

La oss nå skrive vår egen implementering av likhetsmetoden i Personklassen. Vi skal vurdere 4 hovedsaker.

Viktig:
Uansett hvilken klasse som overstyrer equalsmetoden, tar den alltid et Objectobjekt som argument

Scenario 1 : det samme objektet som metoden equalskalles på, sendes også til equalsmetoden. Hvis referansene til det gjeldende objektet og det beståtte objektet er like, må metoden returnere true. Et objekt er lik seg selv.

I koden vil det se slik ut:

Kode Beskrivelse
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


Sammenlign referanser

Scenario 2 : nulloverføres til equalsmetoden — vi har ingenting å sammenligne med. Objektet som equalsmetoden kalles på er definitivt ikke null, så vi må gå tilbake falsei dette tilfellet.

I koden vil det se slik ut:

Kode Beskrivelse
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


Sammenlign referanser


Er det beståtte objektet null?

Scenario 3 : en referanse til et objekt som ikke er en Personblir sendt til equalsmetoden. Er Personobjektet lik ikke- Personobjektet? Det er et spørsmål for utvikleren av klassen Personå bestemme hvordan han eller hun vil.

Men vanligvis må objekter være av samme klasse for å regnes som like. Derfor, hvis noe annet enn et objekt i Personklassen sendes til vår likhetsmetode, vil vi alltid returnere false. Hvordan kan du sjekke typen av et objekt? Det stemmer – ved å bruke instanceofoperatøren.

Slik ser den nye koden vår ut:

Kode Beskrivelse
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


Sammenlign referanser


Er det beståtte objektet null?


Hvis det passerte objektet ikke er enPerson

4. Sammenligning av to Personobjekter

Hva endte vi opp med? Hvis vi har kommet til slutten av metoden, så har vi en Personobjektreferanse som ikke er null. Så vi konverterer det til a Personog sammenligner de relevante interne dataene til begge objektene. Og det er vårt fjerde scenario .

Kode Beskrivelse
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


Sammenlign referanser


Er det beståtte objektet null?


Hvis det beståtte objektet ikke er en Person


Typecasting

Og hvordan sammenligner du to Personobjekter? De er like hvis de har samme navn ( name) og alder ( age). Den endelige koden vil se slik ut:

Kode Beskrivelse
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


Sammenlign referanser


Er det beståtte objektet null?


Hvis det beståtte objektet ikke er en Person


Typecasting

Men det er ikke alt.

Først er navnefeltet en String, så du må sammenligne navnefeltet ved å kalle metoden equals.

this.name.equals(person.name)

For det andre namekan feltet være null: i så fall kan du ikke bruke equalsdet. Du trenger en ekstra sjekk for null:

this.name != null && this.name.equals(person.name)

Når det er sagt, hvis navnefeltet er nulli begge Personobjektene, er navnene fortsatt like.

Koden for det fjerde scenariet kan se slik ut:

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


Hvis alderen ikke er like,
umiddelbart return false

If this.nameer lik null, er det ingen vits i å sammenligne med metoden equals. Her er enten det andre namefeltet lik null, eller så er det ikke.

Sammenlign de to navnefeltene ved å bruke equalsmetoden.


5. hashCode()metode

I tillegg til equalsmetoden, som er ment å utføre en detaljert sammenligning av alle feltene til begge objektene, er det en annen metode som kan brukes for en upresis, men veldig rask sammenligning: hashCode().

Tenk deg at du sorterer alfabetisk en liste med tusenvis av ord, og du må gjentatte ganger sammenligne ordpar. Og ordene er lange, bestående av mange bokstaver. Generelt sett vil en slik sammenligning ta svært lang tid.

Men det kan akselereres. Anta at vi har ord som begynner med forskjellige bokstaver - det er umiddelbart klart at de er forskjellige. Men hvis de begynner med de samme bokstavene, kan vi ennå ikke si hva resultatet blir: ordene kan vise seg å være like eller forskjellige.

Metoden hashCode()fungerer etter et lignende prinsipp. Hvis du kaller det på et objekt, returnerer det et tall - analogt med den første bokstaven i et ord. Dette nummeret har følgende egenskaper:

  • Identiske objekter har alltid samme hashkode
  • Ulike objekter kan ha samme hashkode, eller hashkodene deres kan være forskjellige
  • Hvis objekter har forskjellige hashkoder, er objektene definitivt forskjellige

For å gjøre dette enda mer tydelig, la oss omforme disse egenskapene i form av ord:

  • Identiske ord har alltid de samme første bokstavene.
  • Ulike ord kan ha de samme første bokstavene, eller de første bokstavene kan være forskjellige
  • Hvis ord har forskjellige forbokstaver, er ordene definitivt forskjellige

Den siste egenskapen brukes til å akselerere sammenligning av objekter:

Først beregnes hashkodene til de to objektene. Hvis disse hashkodene er forskjellige, er objektene definitivt forskjellige, og det er ikke nødvendig å sammenligne dem ytterligere.

Men hvis hashkodene er de samme, må vi fortsatt sammenligne objektene ved å bruke likhetsmetoden.



6. Kontrakter i kode

Oppførselen beskrevet ovenfor må implementeres av alle klasser i Java. Under kompilering er det ingen måte å sjekke om objekter sammenlignes riktig.

Java-programmerere har en universell avtale om at hvis de skriver sin egen implementering av equals()-metoden og dermed overstyrer standardimplementeringen (i klassen Object), må de også skrive sin egen implementering av hashCode()metoden på en slik måte at de nevnte reglene er fornøyd.

Denne ordningen kalles en kontrakt .

Hvis du implementerer bare metoden equals()eller bare hashCode()metoden i klassen din, bryter du grovt kontrakten (du har brutt avtalen). Ikke gjør dette.

Hvis andre programmerere bruker koden din, kan det hende at den ikke fungerer som den skal. Dessuten vil du bruke kode som er avhengig av overholdelse av kontraktene ovenfor.

Viktig!

Når du søker etter et element, sammenligner alle Java-samlinger først hashkodene til objekter, og først deretter utfører de en sammenligning ved hjelp av metoden equals.

Det betyr at hvis du gir din egen klasse en equalsmetode, men du ikke skriver din egen hashCode()metode, eller du implementerer den feil, kan det hende at samlinger ikke fungerer riktig med objektene dine.

Du kan for eksempel legge til et objekt i en liste og deretter søke etter det ved hjelp av metoden contains(), men samlingen finner kanskje ikke objektet ditt.