1. Jämföra objekt i Java
I Java kan objekt jämföras både med referens och värde.
Jämför referenser
Om två variabler pekar på samma objekt i minnet är referenserna som lagras i dessa variabler lika. Om du jämför dessa variabler med hjälp av likhetsoperatorn ( ==
), får du sanning, och det resultatet är vettigt. Allt är enkelt här.
Koda | Konsolutgång |
---|---|
|
|
Jämföra efter värde
Men du kan ofta stöta på situationer där två variabler refererar till två distinkta objekt som är identiska. Till exempel två olika strängobjekt som innehåller samma text.
För att avgöra om olika objekt är identiska, använd equals()
metoden. Till exempel:
Koda | Konsolutgång |
---|---|
|
|
Metoden equals
är inte begränsad till String
klassen. Varje klass har det.
Även klasser som du skriver på egen hand, och här är varför.
2. Object
klass
Alla klasser i Java ärver Object
klassen. Javas skapare kom på detta tillvägagångssätt.
Och om en klass ärver Object
klassen, får den alla klassens metoder Object
. Och detta är en stor konsekvens av arv.
Med andra ord, varje klass har klassens metoder Object
, även om deras kod inte nämner dem.
Dessa ärvda metoder inkluderar metoder relaterade till objektjämförelse. Dessa är equals()
och hashCode()
metoderna.
Koda | I verkligheten, här är vad vi kommer att ha: |
---|---|
|
|
I exemplet ovan skapade vi en enkel Person
klass med namn- och åldersparametrar, men inte en enda metod. Men eftersom alla klasser ärver Object
klassen Person
har klassen automatiskt två metoder:
Metod | Beskrivning |
---|---|
|
Jämför det aktuella objektet och det skickade objektet |
|
Returnerar hashkoden för det aktuella objektet |
Det visar sig att absolut varje objekt har equals
metoden, och objekt av olika typer kan jämföras med varandra. Sådan kod kommer att kompilera och fungera perfekt.
Koda | Konsolutgång |
---|---|
|
|
|
|
3. equals()
metod
Metoden equals()
, ärvd från Object
klassen, implementerar den enklaste algoritmen för att jämföra det aktuella objektet med skickade objekt: den jämför bara referenser till objekten.
Du får samma resultat om du bara jämför Person
variabler istället för att anropa equals()
metoden. Exempel:
Koda | Konsolutgång |
---|---|
|
|
När equals
metoden anropas a
jämför den helt enkelt referensen lagrad i a
variabeln med referensen lagrad i variabeln b
.
Jämförelse fungerar dock annorlunda för String
klassen. Varför?
Eftersom personerna som skapade String
klassen skrev sin egen implementering av equals()
metoden.
Implementering av equals()
metoden
Låt oss nu skriva vår egen implementering av equals-metoden i Person
klassen. Vi kommer att överväga 4 huvudfall.
equals
metoden tar den alltid ett Object
objekt som argument
Scenario 1 : samma objekt som equals
metoden anropas på skickas också till equals
metoden. Om referenserna för det aktuella objektet och det skickade objektet är lika, måste metoden returnera true
. Ett föremål är lika med sig självt.
I koden kommer det se ut så här:
Koda | Beskrivning |
---|---|
|
Jämför referenser |
Scenario 2 : null
överförs till equals
metoden — vi har inget att jämföra med. Objektet som equals
metoden anropas på är definitivt inte null, så vi måste återkomma false
i det här fallet.
I koden kommer det se ut så här:
Koda | Beskrivning |
---|---|
|
Jämför referenser Är det skickade objektet null ? |
Scenario 3 : en referens till ett objekt som inte är ett Person
skickas till equals
metoden. Är Person
objektet lika med icke- Person
objektet? Det är en fråga för klassens utvecklare Person
att bestämma hur han eller hon vill.
Men vanligtvis måste objekt vara av samma klass för att anses lika. Därför, om något annat än ett objekt i Person
klassen skickas till vår equals-metod, kommer vi alltid att returnera false
. Hur kan du kontrollera typen av ett objekt? Det stämmer — genom att använda instanceof
operatören.
Så här ser vår nya kod ut:
Koda | Beskrivning |
---|---|
|
Jämför referenser Är det skickade objektet null ? Om det passerade objektet inte är en Person |
4. Jämföra två Person
objekt
Vad slutade vi med? Om vi har nått slutet av metoden har vi en Person
objektreferens som inte är null
. Så vi konverterar det till a Person
och jämför relevanta interna data för båda objekten. Och det är vårt fjärde scenario .
Koda | Beskrivning |
---|---|
|
Jämför referenser Är det skickade objektet null ? Om det skickade objektet inte är en Person Typecasting |
Och hur jämför man två Person
objekt? De är lika om de har samma namn ( name
) och ålder ( age
). Den slutliga koden kommer att se ut så här:
Koda | Beskrivning |
---|---|
|
Jämför referenser Är det skickade objektet null ? Om det skickade objektet inte är en Person Typecasting |
Men det är inte allt.
Först är namnfältet en String
, så du måste jämföra namnfältet genom att anropa equals
metoden.
this.name.equals(person.name)
För det andra name
kan fältet vara null
: i så fall kan du inte använda equals
det. Du behöver en extra check för null
:
this.name != null && this.name.equals(person.name)
Som sagt, om namnfältet finns null
i båda Person
objekten är namnen fortfarande lika.
Koden för det fjärde scenariot kan se ut så här:
|
Om åldrarna inte är lika, omedelbart return false If this.name är lika med null , är det ingen idé att jämföra med equals metoden. Här är antingen det andra name fältet lika med null , eller så är det inte. Jämför de två namnfälten med equals metoden. |
5. hashCode()
metod
Utöver equals
metoden, som är avsedd att utföra en detaljerad jämförelse av alla fält för båda objekten, finns det en annan metod som kan användas för en oprecis men mycket snabb jämförelse: hashCode()
.
Föreställ dig att du sorterar alfabetiskt en lista med tusentals ord, och du måste upprepade gånger jämföra ordpar. Och orden är långa och består av massor av bokstäver. Generellt sett skulle en sådan jämförelse ta väldigt lång tid.
Men det går att påskynda. Anta att vi har ord som börjar med olika bokstäver — det är direkt tydligt att de är olika. Men om de börjar med samma bokstäver kan vi ännu inte säga vad resultatet blir: orden kan visa sig vara lika eller olika.
Metoden hashCode()
fungerar enligt en liknande princip. Om du anropar det på ett objekt, returnerar det någon siffra - analogt med den första bokstaven i ett ord. Detta nummer har följande egenskaper:
- Identiska objekt har alltid samma hashkod
- Olika objekt kan ha samma hashkod, eller så kan deras hashkoder vara olika
- Om objekt har olika hashkoder, är objekten definitivt olika
För att göra detta ännu tydligare, låt oss omformulera dessa egenskaper i termer av ord:
- Identiska ord har alltid samma första bokstäver.
- Olika ord kan ha samma första bokstäver, eller deras första bokstäver kan vara olika
- Om ord har olika första bokstäver, så är orden definitivt olika
Den sista egenskapen används för att påskynda jämförelsen av objekt:
Först beräknas hashkoderna för de två objekten. Om dessa hashkoder är olika, är objekten definitivt olika, och det finns ingen anledning att jämföra dem ytterligare.
Men om hashkoderna är desamma måste vi fortfarande jämföra objekten med metoden equals.
6. Kontrakt i kod
Beteendet som beskrivs ovan måste implementeras av alla klasser i Java. Under kompileringen finns det inget sätt att kontrollera om objekt jämförs korrekt.
Java-programmerare har en universell överenskommelse om att om de skriver sin egen implementering av metoden equals() och därigenom åsidosätter standardimplementeringen (i klassen), så Object
måste de också skriva sin egen implementering av hashCode()
metoden på ett sådant sätt att de ovan nämnda reglerna är nöjd.
Detta arrangemang kallas ett kontrakt .
Om du implementerar endast metoden equals()
eller bara hashCode()
metoden i din klass, bryter du grovt mot avtalet (du har brutit mot avtalet). Gör inte det här.
Om andra programmerare använder din kod kanske den inte fungerar korrekt. Dessutom kommer du att använda kod som är beroende av att ovanstående avtal följs.
När du söker efter ett element jämför alla Java-samlingar först objektens hashkoder och utför först sedan en jämförelse med metoden equals
.
Det betyder att om du ger din egen klass en equals
metod men du inte skriver din egen hashCode()
metod eller om du implementerar den felaktigt, kanske samlingar inte fungerar korrekt med dina objekt.
Du kan till exempel lägga till ett objekt i en lista och sedan söka efter det med metoden, contains()
men samlingen kanske inte hittar ditt objekt.
GO TO FULL VERSION