"Nu zal ik je vertellen over enkele methoden die net zo nuttig zijn:  equals(Object o) & hashCode() ."

"Je hebt je waarschijnlijk al herinnerd dat bij het vergelijken van referentievariabelen in Java niet de objecten zelf worden vergeleken, maar de verwijzingen naar de objecten."

Code Uitleg
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i==j);
i is niet gelijk aan j
De variabelen wijzen naar verschillende objecten.
Ook al bevatten de objecten dezelfde gegevens.
Integer i = new Integer(1);
Integer j = i;
System.out.println(i==j);
ik is gelijk aan j. De variabelen bevatten een verwijzing naar hetzelfde object.

"Ja, dat herinner ik me."

De  gelijken .

"De equals-methode is hier de standaardoplossing. Het doel van de equals-methode is om te bepalen of objecten intern identiek zijn door te vergelijken wat erin is opgeslagen."

"En hoe doet het dat?"

"Het lijkt allemaal sterk op de methode toString()."

De klasse Object heeft zijn eigen implementatie van de methode equals, die eenvoudigweg de referenties vergelijkt:

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

"Geweldig... terug naar dat, zijn we?"

"Hou je kin omhoog! Het is eigenlijk heel lastig."

"Deze methode is gemaakt om ontwikkelaars in staat te stellen deze in hun eigen klassen te overschrijven. Immers, alleen de ontwikkelaar van een klas weet welke gegevens relevant zijn en wat niet bij het vergelijken."

"Kun je een voorbeeld geven?"

"Natuurlijk. Stel dat we een klasse hebben die wiskundige breuken vertegenwoordigt. Het zou er zo uitzien:"

Voorbeeld:
class Fraction
{
private int numerator;
private int denominator;
Fraction(int numerator, int denominator)
{
this.numerator  = numerator;
this.denominator = denominator;
}public boolean equals(Object obj)
{
if (obj==null)
return false;

if (obj.getClass() != this.getClass() )
return false;

Fraction other = (Fraction) obj;
return this.numerator* other.denominator == this.denominator * other.numerator;
}
}
Voorbeeld methodeaanroep:
Fraction one = new Fraction(2,3);
Fraction two = new Fraction(4,6);
System.out.println(one.equals(two));
De methode-aanroep retourneert true.
De breuk 2/3 is gelijk aan de breuk 4/6

"Laten we dit voorbeeld nu eens ontleden."

"We hebben de equals- methode overschreven, dus Fraction- objecten hebben hun eigen implementatie.

"Er zijn verschillende controles in de methode:"

" 1)  Als het ter vergelijking doorgegeven object null is , dan zijn de objecten niet gelijk. Als je de methode equals voor een object kunt aanroepen, dan is het zeker niet null ."

" 2)  Een klassenvergelijking. Als de objecten instanties van verschillende klassen zijn, zullen we ze niet proberen te vergelijken. In plaats daarvan gebruiken we onmiddellijk return false om aan te geven dat dit verschillende objecten zijn."

" 3)  Iedereen herinnert zich uit de tweede klas dat 2/3 gelijk is aan 4/6. Maar hoe controleer je dat?"

2/3 == 4/6
We vermenigvuldigen beide zijden met beide delers (6 en 3), en we krijgen:
6 * 2 == 4 * 3
12 == 12
Algemene regel:
Als
a / b == c / d
Dan is
a * d == c * b

"Dienovereenkomstig casten we in het derde deel van de equals- methode het doorgegeven object naar een breuk en vergelijken we de breuken."

"Begrepen. Als we simpelweg de teller met de teller en de noemer met de noemer vergelijken, dan is 2/3 niet gelijk aan 4/6."

"Nu begrijp ik wat je bedoelde toen je zei dat alleen de ontwikkelaar van een klasse weet hoe hij deze correct moet vergelijken."

"Ja, maar dat is maar de helft van het verhaal.  Er is een andere methode: hashCode(). "

"Alles over de equals-methode is nu logisch, maar waarom hebben we  hashCode () nodig? "

"De hashCode- methode is nodig voor snelle vergelijkingen."

"De equals- methode heeft een groot nadeel: het werkt te traag. Stel, je hebt een Set van miljoenen elementen en moet controleren of er een bepaald object in zit. Hoe doe je dat?"

"Ik zou alle elementen kunnen doorlopen met behulp van een lus en het object met elk object in de set kunnen vergelijken. Tot ik een overeenkomst vind."

"En als het er niet is? We zouden een miljoen vergelijkingen maken om erachter te komen dat het object er niet is? Lijkt dat niet veel?"

"Ja, zelfs ik erken dat dat te veel vergelijkingen zijn. Is er een andere manier?"

"Ja, je kunt hiervoor hashCode () gebruiken.

De methode hashCode () retourneert een specifiek getal voor elk object. De ontwikkelaar van een klasse beslist welk getal wordt teruggegeven, net zoals hij of zij doet voor de equals-methode.

"Laten we eens kijken naar een voorbeeld:"

"Stel je voor dat je een miljoen 10-cijferige nummers hebt. Dan zou je de hashCode van elk nummer de rest kunnen maken na het delen van het nummer door 100."

Hier is een voorbeeld:

Nummer Onze hashcode
1234567890 90
9876554321 21
9876554221 21
9886554121 21

"Ja, dat is logisch. En wat doen we met deze hashCode?"

"In plaats van de cijfers te vergelijken, vergelijken we hun hashCodes . Zo gaat het sneller."

"En we noemen gelijken alleen als hun hashcodes gelijk zijn."

"Ja, dat is sneller. Maar we moeten nog een miljoen vergelijkingen maken. We vergelijken alleen kleinere getallen en we moeten nog steeds gelijken noemen voor alle nummers met overeenkomende hashcodes."

"Nee, je kunt wegkomen met een veel kleiner aantal vergelijkingen."

"Stel je voor dat onze Set nummers gegroepeerd of gesorteerd op hashCode opslaat (ze op deze manier sorteren is in feite groeperen, aangezien nummers met dezelfde hashCode naast elkaar staan). Dan kun je heel snel en gemakkelijk irrelevante groepen weggooien. Het is genoeg om eenmaal per groep te controleren of de hashCode overeenkomt met de hashCode van het object."

"Stel je voor dat je een student bent die op zoek is naar een vriend die je van gezicht kunt herkennen en waarvan we weten dat hij in slaapzaal 17 woont. Dan ga je gewoon naar elke slaapzaal op de universiteit en vraag je: 'Is dit slaapzaal 17?' Als dat niet zo is, negeer je iedereen in de slaapzaal en ga je naar de volgende. Als het antwoord 'ja' is, begin je langs elk van de kamers te lopen, op zoek naar je vriend.'

"In dit voorbeeld is het slaapzaalnummer (17) de hashCode."

"Een ontwikkelaar die een hashCode-functie implementeert, moet het volgende weten:"

A)  twee verschillende objecten kunnen dezelfde hashCode hebben  (verschillende mensen kunnen in dezelfde slaapzaal wonen)

B)  objecten die hetzelfde zijn  ( volgens de equals methodemoeten dezelfde hashCode hebben. .

C)  hashcodes moeten zo worden gekozen dat er niet veel verschillende objecten zijn met dezelfde hashCode.  Als dat zo is, gaan de potentiële voordelen van hashcodes verloren (je gaat naar slaapzaal 17 en ontdekt dat de helft van de universiteit daar woont. Balen!).

"En nu het allerbelangrijkste. Als je de equals- methode overschrijft, moet je absoluut de hashCode ()-methode overschrijven en voldoen aan de drie hierboven beschreven regels.

"De reden is deze: in Java worden objecten in een verzameling altijd vergeleken/opgehaald met behulp van hashCode() voordat ze worden vergeleken/opgehaald met  behulp van gelijken . wordt niet eens gebeld.

"Als we in ons breukenvoorbeeld de hashcode gelijk zouden maken aan de teller, zouden de breuken 2/3 en 4/6 verschillende hashcodes hebben. De breuken zijn hetzelfde en de methode gelijken zegt dat ze hetzelfde zijn, maar hun hashcodes zeggen ze zijn verschillend. En als we vergelijken met hashCode voordat we vergelijken met gelijken, dan concluderen we dat de objecten verschillend zijn en halen we de methode gelijken nooit.'

Hier is een voorbeeld:

HashSet<Fraction>set = new HashSet<Fraction>();
set.add(new Fraction(2,3));System.out.println( set.contains(new Fraction(4,6)) );
Als de methode hashCode()  de teller van de breuken retourneert, is het resultaat  false .
En het «nieuwe Fraction(4,6) » object zal niet in de collectie gevonden worden.

"Dus wat is de juiste manier om hashCode voor breuken te implementeren?"

"Hier moet je onthouden dat equivalente breuken dezelfde hashCode moeten hebben."

" Versie 1 : de hashCode is gelijk aan het resultaat van deling door gehele getallen."

"Voor 7/5 en 6/5 zou dit 1 zijn."

"Voor 4/5 en 3/5 zou dit 0 zijn."

"Maar deze optie is slecht geschikt voor het vergelijken van breuken die opzettelijk kleiner zijn dan 1. De hashCode (resultaat van deling door gehele getallen) zal altijd 0 zijn."

" Versie 2 : de hashCode is gelijk aan het resultaat van de deling door een geheel getal van de noemer door de teller."

"Deze optie is geschikt voor gevallen waarin de breuk kleiner is dan 1. Als de breuk kleiner is dan 1, dan is de inverse groter dan 1. En als we alle breuken omkeren, worden de vergelijkingen op geen enkele manier beïnvloed."

"Onze definitieve versie combineert beide oplossingen:"

public int hashCode()
{
return numerator/denominator + denominator/numerator;
}

Laten we het testen met 2/3 en 4/6. Ze moeten identieke hashcodes hebben:

Fractie 2/3 Fractie 4/6
teller noemer 2 / 3 == 0 4 / 6 == 0
noemer / teller 3 / 2 == 1 6 / 4 == 1
teller / noemer
+
noemer / teller
0 + 1 == 1 0 + 1 == 1

"Dat is het voor nu."

'Bedankt, Ellie. Dat was echt interessant.'