"Nå skal jeg fortelle deg om noen metoder som er like nyttige: equals(Object o) & hashCode() ."
"Du har sikkert allerede husket at i Java, når man sammenligner referansevariabler, blir ikke selve objektene sammenlignet, men snarere referansene til objektene."
Kode | Forklaring |
---|---|
|
i er ikke lik j Variablene peker på forskjellige objekter. Selv om objektene inneholder samme data. |
|
jeg er lik j. Variablene inneholder en referanse til samme objekt. |
"Ja, det husker jeg."
De likeverdige .
"lik metoden er standardløsningen her. Formålet med likhetsmetoden er å finne ut om objekter er internt identiske ved å sammenligne hva som er lagret inne i dem."
"Og hvordan gjør den det?""Det hele er veldig likt toString()-metoden."
Object-klassen har sin egen implementering av equals-metoden, som ganske enkelt sammenligner referansene:
public boolean equals(Object obj)
{
return (this == obj);
}
"Flott... tilbake til det igjen, er vi?"
"Hold haken oppe! Det er faktisk veldig vanskelig."
"Denne metoden ble laget for å tillate utviklere å overskrive den i sine egne klasser. Tross alt er det bare en klasseutvikler som vet hvilke data som er relevante og hva som ikke er det når de sammenligner."
"Kan du gi et eksempel?"
"Jada. Anta at vi har en klasse som representerer matematiske brøker. Den vil se slik ut:"
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;
}
}
Eksempel på metodekall: |
---|
Fraction one = new Fraction(2,3); Fraction two = new Fraction(4,6); System.out.println(one.equals(two)); |
Metodekallet vil returnere sant. Brøken 2/3 er lik brøkdelen 4/6 |
"Nå, la oss dissekere dette eksemplet."
"Vi overstyrte likhetsmetoden , så Fraksjonsobjekter vil ha sin egen implementering.
"Det er flere kontroller i metoden:"
" 1) Hvis objektet som sendes til sammenligning er null , er ikke objektene like. Hvis du kan kalle likhetsmetoden på et objekt, er det definitivt ikke null ."
" 2) En klassesammenligning. Hvis objektene er forekomster av forskjellige klasser, vil vi ikke prøve å sammenligne dem. I stedet vil vi umiddelbart bruke return false for å indikere at disse er forskjellige objekter."
" 3) Alle husker fra andre klasse at 2/3 er lik 4/6. Men hvordan sjekker du det?"
2/3 == 4/6 |
---|
Vi multipliserer begge sider med begge divisorene (6 og 3), og vi får: |
6 * 2 == 4 * 3 |
12 == 12 |
Generell regel: |
Hvis a / b == c / d Så a * d == c * b |
"I den tredje delen av likhetsmetoden kaster vi følgelig det passerte objektet til en brøk og sammenligner brøkene."
"Skjønner det. Hvis vi bare sammenligner telleren med telleren og nevneren med nevneren, så er ikke 2/3 lik 4/6."
"Nå forstår jeg hva du mente når du sa at bare en klasses utvikler vet hvordan de skal sammenligne det riktig."
"Ja, men det er bare halve historien. Det er en annen metode: hashCode(). "
"Alt med equals-metoden gir mening nå, men hvorfor trenger vi hashCode ()? "
" HashCode- metoden er nødvendig for raske sammenligninger."
" likningsmetoden har en stor ulempe: den fungerer for sakte. Anta at du har et sett med millioner av elementer og trenger å sjekke om det inneholder et spesifikt objekt. Hvordan gjør du det?"
"Jeg kunne bla gjennom alle elementene ved hjelp av en løkke og sammenligne objektet med hvert objekt i settet. Helt til jeg finner en match."
"Og hvis det ikke er der? Vi ville utført en million sammenligninger bare for å finne ut at objektet ikke er der? Virker det ikke som mye?"
"Ja, selv jeg innser at det er for mange sammenligninger. Er det en annen måte?"
"Ja, du kan bruke hashCode () for dette.
Metoden hashCode ( ) returnerer et spesifikt tall for hvert objekt. En klasses utvikler bestemmer hvilket nummer som returneres, akkurat som han eller hun gjør for lik metoden.
"La oss se på et eksempel:"
"Se for deg at du har en million 10-sifrede tall. Deretter kan du få hvert talls hashkode til å være resten etter å ha delt tallet med 100."
Her er et eksempel:
Antall | Vår hashkode |
---|---|
1234567890 | 90 |
9876554321 | 21 |
9876554221 | 21 |
9886554121 | 21 |
"Ja, det er fornuftig. Og hva gjør vi med denne hashkoden?"
"I stedet for å sammenligne tallene, sammenligner vi hashkodene deres . Det er raskere på den måten."
"Og vi kaller likeverdige bare hvis hashkodene deres er like."
"Ja, det er raskere. Men vi må fortsatt gjøre en million sammenligninger. Vi sammenligner bare mindre tall, og vi må fortsatt ringe like for alle tall med samsvarende hashkoder."
"Nei, du kan slippe unna med et mye mindre antall sammenligninger."
"Tenk deg at settet vårt lagrer tall gruppert eller sortert etter hashCode (å sortere dem på denne måten er i hovedsak å gruppere dem, siden tall med samme hashCode vil være ved siden av hverandre). Da kan du veldig raskt og enkelt forkaste irrelevante grupper. Det er nok for å sjekke én gang per gruppe for å se om dens hashCode samsvarer med objektets hashCode."
"Tenk deg at du er en student som leter etter en venn du kan kjenne igjen på synet og som vi vet bor i Dorm 17. Så går du bare til alle sovesaler på universitetet og spør: 'Er dette Dorm 17?' Hvis det ikke er det, ignorerer du alle på hybelen og går videre til neste. Hvis svaret er 'ja', begynner du å gå forbi hvert av rommene og lete etter vennen din."
"I dette eksemplet er hybelnummeret (17) hashkoden."
"En utvikler som implementerer en hashCode-funksjon må vite følgende:"
A) to forskjellige objekter kan ha samme hashCode (forskjellige personer kan bo i samme hybel)
B) objekter som er like ( i henhold til likhetsmetoden ) må ha samme hashCode. .
C) hash-koder må velges slik at det ikke er mange forskjellige objekter med samme hashCode. Hvis det er det, går hashkoders potensielle fordeler tapt (du kommer til Dorm 17 og finner ut at halve universitetet bor der. Bummer!).
"Og nå det viktigste. Hvis du overstyrer equals- metoden, må du absolutt overstyre hashCode ()-metoden og overholde de tre reglene beskrevet ovenfor.
"Årsaken er denne: i Java blir objekter i en samling alltid sammenlignet/hentet ved hjelp av hashCode() før de sammenlignes/hentes ved bruk av likes. Og hvis identiske objekter har forskjellige hashkoder, vil objektene bli ansett som forskjellige og lik metoden. vil ikke engang bli oppringt.
"I vårt brøkeksempel, hvis vi gjorde hashkoden lik telleren, ville brøkene 2/3 og 4/6 ha forskjellige hashkoder. Brøkene er de samme og lik metoden sier at de er like, men hashkodene deres sier de er forskjellige. Og hvis vi sammenligner ved å bruke hashCode før vi sammenligner ved å bruke likes, så konkluderer vi med at objektene er forskjellige og vi kommer aldri til likhetsmetoden engang."
Her er et eksempel:
HashSet<Fraction>set = new HashSet<Fraction>(); set.add(new Fraction(2,3)); System.out.println( set.contains(new Fraction(4,6)) ); |
Hvis hashCode() -metoden returnerer brøkenes teller, vil resultatet være false . Og «nye Fraction(4,6) »-objektet vil ikke bli funnet i samlingen. |
"Så hva er den riktige måten å implementere hashCode for brøker?"
"Her må du huske at ekvivalente brøker må ha samme hashkode."
" Versjon 1 : hashkoden er lik resultatet av heltallsdivisjon."
"For 7/5 og 6/5 vil dette være 1."
"For 4/5 og 3/5 vil dette være 0."
"Men dette alternativet er dårlig egnet for å sammenligne brøker som er bevisst mindre enn 1. HashCode (resultat av heltallsdivisjon) vil alltid være 0."
" Versjon 2 : hashkoden er lik resultatet av heltallsdeling av nevneren med telleren."
"Dette alternativet er egnet for tilfeller der brøken er mindre enn 1. Hvis brøken er mindre enn 1, så er dens inverse større enn 1. Og hvis vi inverterer alle brøkene, påvirkes ikke sammenligningene på noen måte."
"Vår endelige versjon kombinerer begge løsningene:"
public int hashCode()
{
return numerator/denominator + denominator/numerator;
}
La oss teste den med 2/3 og 4/6. De skal ha identiske hashkoder:
Brøk 2/3 | Brøk 4/6 | |
---|---|---|
teller / nevner | 2 / 3 == 0 | 4/6 == 0 |
nevner / teller | 3 / 2 == 1 | 6 / 4 == 1 |
teller / nevner + nevner / teller |
0 + 1 == 1 | 0 + 1 == 1 |
"Det er alt for nå."
"Takk, Ellie. Det var veldig interessant."
GO TO FULL VERSION