1. Sammenligning af objekter i Java

I Java kan objekter sammenlignes både efter reference og efter værdi.

Sammenligning af referencer

Hvis to variabler peger på det samme objekt i hukommelsen, så er referencerne gemt i disse variable ens. Hvis du sammenligner disse variable ved hjælp af lighedsoperatoren ( ==), får du sandhed, og det resultat giver mening. Alt er enkelt her.

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


true

Sammenligning efter værdi

Men du kan ofte støde på situationer, hvor to variable refererer til to forskellige objekter, der er identiske. For eksempel to forskellige strengobjekter, der indeholder den samme tekst.

For at afgøre, om forskellige objekter er identiske, skal du bruge equals()metoden. For eksempel:

Kode Konsoludgang
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 begrænset til Stringklassen. Hver klasse har det.

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



2. Objectklasse

Alle klasser i Java arver Objectklassen. Javas skabere fandt på denne tilgang.

Og hvis en klasse arver Objectklassen, får den alle klassens metoder Object. Og det er en stor konsekvens af arv.

Med andre ord, hver klasse har klassens metoder Object, selvom deres kode ikke nævner dem.

Disse nedarvede metoder omfatter metoder relateret til objektsammenligning. Disse er equals()og hashCode()metoderne.

Kode I virkeligheden er det her, 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 oprettede vi en simpel Personklasse med navn og aldersparametre, men ikke en enkelt metode. Men fordi alle klasser arver Objectklassen, Personhar klassen automatisk to metoder:

Metode Beskrivelse
boolean equals(Object obj)
Sammenligner det aktuelle objekt og det beståede objekt
int hashCode()
Returnerer hashkoden for det aktuelle objekt

Det viser sig, at absolut alle objekter har equalsmetoden, og objekter af forskellige typer kan sammenlignes med hinanden. En sådan kode vil kompilere og fungere perfekt.

Kode Konsoludgang
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(), der er arvet fra Objectklassen, implementerer den enkleste algoritme til at sammenligne det aktuelle objekt med beståede objekter: den sammenligner blot referencer til objekterne.

Du får det samme resultat, hvis du bare sammenligner Personvariable i stedet for at kalde equals()metoden. Eksempel:

Kode Konsoludgang
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 kaldes på a, sammenligner den blot referencen gemt i avariablen med referencen gemt i bvariablen.

Sammenligning fungerer dog anderledes for Stringklassen. Hvorfor?

Fordi folkene, der skabte Stringklassen, skrev deres egen implementering af equals()metoden.

Implementering af equals()metoden

Lad os nu skrive vores egen implementering af equals-metoden i Personklassen. Vi vil overveje 4 hovedsager.

Vigtig:
Uanset hvilken klasse der tilsidesætter equalsmetoden, tager den altid et Objectobjekt som et argument

Scenarie 1 : det samme objekt, som metoden equalskaldes på, videregives også til equalsmetoden. Hvis referencerne for det aktuelle objekt og det beståede objekt er ens, skal metoden returnere true. Et objekt er lig med sig selv.

I koden vil det se sådan ud:

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

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


Sammenlign referencer

Scenarie 2 : nullvideregives til equalsmetoden - vi har intet at sammenligne med. Objektet, som equalsmetoden kaldes på, er bestemt ikke null, så vi skal vende tilbage falsei dette tilfælde.

I koden vil det se sådan ud:

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 referencer


Er det beståede objekt null?

Scenarie 3 : en reference til et objekt, der ikke er en, Personvideregives til equalsmetoden. Er Personobjektet lig med ikke- Personobjektet? Det er et spørgsmål for udvikleren af ​​klassen Personat beslutte, hvordan han eller hun vil.

Men normalt skal objekter være af samme klasse for at blive betragtet som lige. Derfor, hvis noget andet end et objekt i Personklassen overføres til vores equals-metode, vil vi altid returnere false. Hvordan kan du kontrollere typen af ​​et objekt? Det er rigtigt - ved at bruge instanceofoperatøren.

Sådan ser vores nye kode ud:

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 referencer


Er det beståede objekt null?


Hvis det passerede objekt ikke er enPerson

4. Sammenligning af to Personobjekter

Hvad endte vi med? Hvis vi er nået til slutningen af ​​metoden, så har vi en Personobjektreference, der ikke er null. Så vi konverterer det til a Personog sammenligner de relevante interne data for begge objekter. Og det er vores fjerde scenarie .

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 referencer


Er det beståede objekt null?


Hvis det beståede objekt ikke er en Person


Typecasting

Og hvordan sammenligner man to Personobjekter? De er ens, hvis de har samme navn ( name) og alder ( age). Den endelige kode vil se sådan ud:

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 referencer


Er det beståede objekt null?


Hvis det beståede objekt ikke er en Person


Typecasting

Men det er ikke alt.

Først er navnefeltet et String, så du skal sammenligne navnefeltet ved at kalde equalsmetoden.

this.name.equals(person.name)

For det andet namekan feltet være null: i så fald kan du ikke kalde equalspå det. Du skal bruge en ekstra check til null:

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

Når det er sagt, hvis navnefeltet er nulli begge Personobjekter, så er navnene stadig ens.

Koden til det fjerde scenarie kan se sådan ud:

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 aldre ikke er ens,
umiddelbart return false

If this.nameer lig med null, er der ingen mening i at sammenligne ved hjælp af equalsmetoden. Her er enten det andet namefelt lig med null, eller også er det ikke.

Sammenlign de to navnefelter ved hjælp af equalsmetoden.


5. hashCode()metode

Ud over equalsmetoden, som er beregnet til at udføre en detaljeret sammenligning af alle felterne for begge objekter, er der en anden metode, der kan bruges til en upræcis, men meget hurtig sammenligning: hashCode().

Forestil dig, at du alfabetisk sorterer en liste med tusindvis af ord, og du skal gentagne gange sammenligne ordpar. Og ordene er lange og består af masser af bogstaver. Generelt vil en sådan sammenligning tage meget lang tid.

Men det kan accelereres. Antag, at vi har ord, der begynder med forskellige bogstaver - det er umiddelbart klart, at de er forskellige. Men hvis de begynder med de samme bogstaver, så kan vi endnu ikke sige, hvad resultatet bliver: ordene kan vise sig at være ens eller forskellige.

Metoden hashCode()fungerer efter et lignende princip. Hvis du kalder det på et objekt, returnerer det et eller andet tal - analogt med det første bogstav i et ord. Dette nummer har følgende egenskaber:

  • Identiske objekter har altid den samme hashkode
  • Forskellige objekter kan have den samme hashkode, eller deres hashkoder kan være forskellige
  • Hvis objekter har forskellige hashkoder, så er objekterne helt sikkert forskellige

For at gøre dette endnu mere klart, lad os omformulere disse egenskaber i form af ord:

  • Identiske ord har altid de samme første bogstaver.
  • Forskellige ord kan have de samme første bogstaver, eller deres første bogstaver kan være forskellige
  • Hvis ord har forskellige første bogstaver, så er ordene helt sikkert forskellige

Den sidste egenskab bruges til at fremskynde sammenligning af objekter:

Først beregnes hashkoderne for de to objekter. Hvis disse hashkoder er forskellige, så er objekterne bestemt forskellige, og der er ingen grund til at sammenligne dem yderligere.

Men hvis hashkoderne er de samme, så skal vi stadig sammenligne objekterne ved hjælp af equals-metoden.



6. Kontrakter i kode

Den ovenfor beskrevne adfærd skal implementeres af alle klasser i Java. Under kompilering er der ingen måde at kontrollere, om objekter sammenlignes korrekt.

Java-programmører har en universel aftale om, at hvis de skriver deres egen implementering af equals()-metoden og derved tilsidesætter standardimplementeringen (i klassen Object), skal de også skrive deres egen implementering af metoden hashCode()på en sådan måde, at de førnævnte regler er tilfreds.

Denne ordning kaldes en kontrakt .

Hvis du kun implementerer equals()eller kun hashCode()metoden i din klasse, er du i grov overtrædelse af kontrakten (du har brudt aftalen). Gør ikke dette.

Hvis andre programmører bruger din kode, fungerer den muligvis ikke korrekt. Hvad mere er, vil du bruge kode, der er afhængig af overholdelse af ovenstående kontrakter.

Vigtig!

Når du søger efter et element, sammenligner alle Java-samlinger først objekternes hashkoder og udfører først derefter en sammenligning ved hjælp af metoden equals.

Det betyder, at hvis du giver din egen klasse en equalsmetode, men du ikke skriver din egen hashCode()metode, eller du implementerer den forkert, så fungerer samlinger muligvis ikke korrekt med dine objekter.

For eksempel kan du tilføje et objekt til en liste og derefter søge efter det ved hjælp af metoden contains(), men samlingen finder muligvis ikke dit objekt.