equals
ay hashCode
malapit na nauugnay sa isa't isa, at na ipinapayong i-override ang parehong mga pamamaraan na ito sa kanilang mga klase nang tuluy-tuloy. Ang isang bahagyang mas maliit na bilang ay nakakaalam kung bakit ganito at kung anong malungkot na kahihinatnan ang maaaring mangyari kung nilabag ang panuntunang ito. Iminumungkahi kong isaalang-alang ang konsepto ng mga pamamaraang ito, ulitin ang kanilang layunin at maunawaan kung bakit sila konektado. Isinulat ko ang artikulong ito, tulad ng nauna tungkol sa pag-load ng mga klase, para sa aking sarili upang sa wakas ay maihayag ang lahat ng mga detalye ng isyu at hindi na bumalik sa mga mapagkukunan ng third-party. Samakatuwid, ako ay natutuwa sa constructive criticism, dahil kung may mga puwang sa isang lugar, dapat silang alisin. Ang artikulo, sayang, naging medyo mahaba.
katumbas ng override rules
Ang isang pamamaraanequals()
ay kinakailangan sa Java upang kumpirmahin o tanggihan ang katotohanan na ang dalawang bagay na may parehong pinagmulan ay lohikal na magkapareho . Iyon ay, kapag naghahambing ng dalawang bagay, kailangang maunawaan ng programmer kung ang kanilang mga makabuluhang field ay katumbas . Hindi kinakailangan na ang lahat ng mga patlang ay dapat na magkapareho, dahil ang pamamaraan ay equals()
nagpapahiwatig ng lohikal na pagkakapantay-pantay . Ngunit kung minsan ay walang partikular na pangangailangan na gamitin ang pamamaraang ito. Tulad ng sinasabi nila, ang pinakamadaling paraan upang maiwasan ang mga problema gamit ang isang partikular na mekanismo ay hindi ang paggamit nito. Dapat ding tandaan na sa sandaling masira mo ang isang kontrata, equals
mawawalan ka ng kontrol sa pag-unawa kung paano makikipag-ugnayan ang ibang mga bagay at istruktura sa iyong bagay. At kasunod na paghahanap ng sanhi ng error ay magiging napakahirap.
Kailan hindi dapat i-override ang paraang ito
- Kapag ang bawat instance ng isang klase ay natatangi. Sa mas malaking lawak, nalalapat ito sa mga klaseng iyon na nagbibigay ng partikular na gawi sa halip na idinisenyo upang gumana sa data. Tulad, halimbawa, bilang klase
- Kung sa katunayan ang klase ay hindi kinakailangan upang matukoy ang pagkakapareho ng mga pagkakataon nito. Halimbawa, para sa isang klase
- Kapag ang klase na iyong pinalawig ay mayroon nang sariling pagpapatupad ng pamamaraan
equals
at ang pag-uugali ng pagpapatupad na ito ay nababagay sa iyo. Halimbawa, para sa mga klase - At sa wakas, hindi na kailangang i-override
equals
kapag ang saklaw ng iyong klase ayprivate
opackage-private
at sigurado ka na ang pamamaraang ito ay hindi kailanman matatawag.
Thread
. Para sa kanila equals
, ang pagpapatupad ng pamamaraang ibinigay ng klase Object
ay higit pa sa sapat. Ang isa pang halimbawa ay ang mga klase ng enum ( Enum
).
java.util.Random
ay hindi na kailangang ihambing ang mga pagkakataon ng klase sa isa't isa, na tinutukoy kung maaari nilang ibalik ang parehong pagkakasunud-sunod ng mga random na numero. Dahil lang sa katangian ng klase na ito ay hindi man lang nagpapahiwatig ng ganoong pag-uugali.
Set
, List
, Map
ang pagpapatupad equals
ay nasa AbstractSet
, AbstractList
at AbstractMap
ayon sa pagkakabanggit.
katumbas ng kontrata
Kapag nag-o-override ng isang paraan,equals
dapat sumunod ang developer sa mga pangunahing panuntunang tinukoy sa detalye ng wikang Java.
- Reflexivity para sa anumang ibinigay na halaga
- Simetrya para sa anumang ibinigay na mga halaga
- Transitivity para sa anumang ibinigay na mga halaga
- Hindi pagbabago para sa anumang ibinigay na mga halaga,
- Paghahambing null para sa anumang ibinigay na halaga
x
, ang expression x.equals(x)
ay dapat bumalik true
.
Given - ibig sabihin ganyan
x != null
x
at y
, x.equals(y)
ay dapat bumalik true
lamang kung ito ay y.equals(x)
bumalik true
.
x
, y
at z
, kung x.equals(y)
bumalik true
at y.equals(z)
bumalik true
, x.equals(z)
dapat ibalik ang halaga true
.
x
at ibabalik ng y
paulit-ulit na tawag ang halaga ng nakaraang tawag sa paraang ito, sa kondisyon na ang mga field na ginamit upang ihambing ang dalawang bagay ay hindi nagbabago sa pagitan ng mga tawag.x.equals(y)
x
ang tawag x.equals(null)
ay dapat bumalik false
.
katumbas ng paglabag sa kontrata
Maraming mga klase, tulad ng mga mula sa Java Collections Framework, ay nakasalalay sa pagpapatupad ng pamamaraanequals()
, kaya hindi mo ito dapat pabayaan, dahil Ang paglabag sa kontrata ng pamamaraang ito ay maaaring humantong sa hindi makatwiran na operasyon ng aplikasyon, at sa kasong ito ay magiging mahirap hanapin ang dahilan. Ayon sa prinsipyo ng reflexivity , ang bawat bagay ay dapat na katumbas ng sarili nito. Kung nilabag ang prinsipyong ito, kapag nagdagdag kami ng isang bagay sa koleksyon at pagkatapos ay hinanap ito gamit ang pamamaraan, contains()
hindi namin mahahanap ang bagay na idinagdag namin sa koleksyon. Ang kondisyon ng symmetry ay nagsasaad na ang anumang dalawang bagay ay dapat na pantay-pantay anuman ang pagkakasunud-sunod kung saan sila inihambing. Halimbawa, kung mayroon kang isang klase na naglalaman lamang ng isang field ng uri ng string, magiging mali na ihambing ang equals
field na ito sa isang string sa isang paraan. kasi sa kaso ng baligtad na paghahambing, palaging ibabalik ng pamamaraan ang halaga false
.
// Нарушение симметричности
public class SomeStringify {
private String s;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof SomeStringify) {
return s.equals(((SomeStringify) o).s);
}
// нарушение симметричности, классы разного происхождения
if (o instanceof String) {
return s.equals(o);
}
return false;
}
}
//Правильное определение метода equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
return o instanceof SomeStringify &&
((SomeStringify) o).s.equals(s);
}
Mula sa kondisyon ng transitivity sumusunod na kung alinman sa dalawa sa tatlong mga bagay ay pantay, kung gayon sa kasong ito ang lahat ng tatlo ay dapat na pantay. Ang prinsipyong ito ay madaling malalabag kapag kinakailangan na palawigin ang isang partikular na base class sa pamamagitan ng pagdaragdag ng isang makabuluhang bahagi dito . Halimbawa, sa isang klase Point
na may mga coordinate x
at y
kailangan mong idagdag ang kulay ng punto sa pamamagitan ng pagpapalawak nito. Upang gawin ito, kakailanganin mong magdeklara ng klase ColorPoint
na may naaangkop na field color
. Kaya, kung sa pinalawig na klase tinatawag namin ang equals
paraan ng magulang, at sa magulang ay ipinapalagay namin na ang mga coordinate lamang x
at inihambing y
, kung gayon ang dalawang punto ng magkakaibang kulay ngunit may parehong mga coordinate ay ituturing na pantay, na hindi tama. Sa kasong ito, kinakailangan upang turuan ang nagmula na klase upang makilala ang mga kulay. Upang gawin ito, maaari kang gumamit ng dalawang pamamaraan. Ngunit ang isa ay lalabag sa tuntunin ng mahusay na proporsyon , at ang pangalawang - transitivity .
// Первый способ, нарушая симметричность
// Метод переопределен в классе ColorPoint
@Override
public boolean equals(Object o) {
if (!(o instanceof ColorPoint)) return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
point.equals(colorPoint)
Sa kasong ito, ibabalik ng tawag ang halaga true
, at colorPoint.equals(point)
babalik ang paghahambing false
, dahil inaasahan ang isang bagay ng "nito" klase. Kaya, ang panuntunan ng mahusay na proporsyon ay nilabag. Ang pangalawang paraan ay nagsasangkot ng paggawa ng "bulag" na pagsusuri sa kaso kapag walang data tungkol sa kulay ng punto, ibig sabihin, mayroon kaming klase Point
. O suriin ang kulay kung magagamit ang impormasyon tungkol dito, iyon ay, ihambing ang isang bagay ng klase ColorPoint
.
// Метод переопределен в классе ColorPoint
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
// Слепая проверка
if (!(o instanceof ColorPoint))
return super.equals(o);
// Полная проверка, включая цвет точки
return super.equals(o) && ((ColorPoint) o).color == color;
}
Ang prinsipyo ng transitivity ay nilabag dito bilang mga sumusunod. Sabihin nating mayroong kahulugan ng mga sumusunod na bagay:
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
Kaya, kahit na ang pagkakapantay-pantay p1.equals(p2)
at nasiyahan p2.equals(p3)
, p1.equals(p3)
ibabalik nito ang halaga false
. Kasabay nito, ang pangalawang paraan, sa palagay ko, ay mukhang hindi gaanong kaakit-akit, dahil Sa ilang mga kaso, maaaring mabulag ang algorithm at hindi ganap na maisagawa ang paghahambing, at maaaring hindi mo alam ang tungkol dito. Kaunting tula Sa pangkalahatan, sa pagkakaintindi ko, walang konkretong solusyon sa problemang ito. May isang opinyon mula sa isang awtoritatibong may-akda na nagngangalang Kay Horstmann na maaari mong palitan ang paggamit ng operator instanceof
ng isang method call getClass()
na nagbabalik sa klase ng object at, bago mo simulan ang paghahambing ng mga bagay sa kanilang sarili, siguraduhin na ang mga ito ay pareho ang uri , at huwag pansinin ang katotohanan ng kanilang karaniwang pinagmulan. Kaya, ang mga patakaran ng simetrya at transitivity ay masisiyahan. Ngunit sa parehong oras, sa kabilang panig ng barikada ay nakatayo ang isa pang may-akda, na hindi gaanong iginagalang sa malawak na mga bilog, si Joshua Bloch, na naniniwala na ang pamamaraang ito ay lumalabag sa prinsipyo ng pagpapalit ng Barbara Liskov. Ang prinsipyong ito ay nagsasaad na "ang code sa pagtawag ay dapat tratuhin ang isang base class sa parehong paraan tulad ng mga subclass nito nang hindi nalalaman ito . " At sa solusyon na iminungkahi ni Horstmann, ang prinsipyong ito ay malinaw na nilabag, dahil ito ay nakasalalay sa pagpapatupad. Sa madaling salita, malinaw na madilim ang usapin. Dapat ding tandaan na nilinaw ni Horstmann ang panuntunan para sa paglalapat ng kanyang diskarte at nagsusulat sa simpleng Ingles na kailangan mong magpasya sa isang diskarte kapag nagdidisenyo ng mga klase, at kung ang equality testing ay isasagawa lamang ng superclass, magagawa mo ito sa pamamagitan ng pagsasagawa ang operasyon instanceof
. Kung hindi, kapag ang mga semantika ng tseke ay nagbabago depende sa nagmula na klase at ang pagpapatupad ng pamamaraan ay kailangang ilipat pababa sa hierarchy, dapat mong gamitin ang pamamaraan getClass()
. Si Joshua Bloch, naman, ay nagmumungkahi na abandunahin ang mana at gumamit ng komposisyon ng bagay sa pamamagitan ng pagsasama ng isang ColorPoint
klase sa klase Point
at pagbibigay ng paraan ng pag-access asPoint()
upang makakuha ng partikular na impormasyon tungkol sa punto. Maiiwasan nito ang paglabag sa lahat ng mga patakaran, ngunit, sa palagay ko, gagawin nitong mas mahirap na maunawaan ang code. Ang ikatlong opsyon ay ang paggamit ng awtomatikong henerasyon ng katumbas na paraan gamit ang IDE. Ang ideya, sa pamamagitan ng paraan, ay nagpaparami ng henerasyon ng Horstmann, na nagbibigay-daan sa iyong pumili ng isang diskarte para sa pagpapatupad ng isang pamamaraan sa isang superclass o sa mga inapo nito. Sa wakas, ang susunod na panuntunan sa pagkakapare-pareho ay nagsasaad na kahit na ang mga bagay ay x
hindi y
nagbabago, ang pagtawag sa kanila muli x.equals(y)
ay dapat na ibalik ang parehong halaga tulad ng dati. Ang panghuling tuntunin ay walang bagay na dapat na katumbas ng null
. Ang lahat ay malinaw dito null
- ito ay kawalan ng katiyakan, ang bagay ba ay katumbas ng kawalan ng katiyakan? Hindi malinaw, ibig sabihin false
.
Pangkalahatang algorithm para sa pagtukoy ng mga katumbas
- Suriin ang pagkakapantay-pantay ng mga sanggunian sa bagay
this
at mga parameter ng pamamaraano
.if (this == o) return true;
- Suriin kung ang link ay tinukoy
o
, ibig sabihin, kung ito aynull
.
Kung sa hinaharap, kapag naghahambing ng mga uri ng bagay, ang operator ay gagamitininstanceof
, ang item na ito ay maaaring laktawan, dahil ang parameter na ito ay bumalikfalse
sa kasong itonull instanceof Object
. - Ihambing ang mga uri ng bagay
this
gamito
ang isang operatorinstanceof
o pamamaraangetClass()
, na ginagabayan ng paglalarawan sa itaas at ng iyong sariling intuwisyon. - Kung ang isang paraan
equals
ay na-override sa isang subclass, siguraduhing tumawagsuper.equals(o)
- I-convert ang uri ng parameter
o
sa kinakailangang klase. - Magsagawa ng paghahambing ng lahat ng makabuluhang object field:
- para sa mga primitive na uri (maliban
float
sa atdouble
), gamit ang operator==
- para sa mga patlang ng sanggunian kailangan mong tawagan ang kanilang pamamaraan
equals
- para sa mga array, maaari mong gamitin ang cyclic iteration o ang paraan
Arrays.equals()
- para sa mga uri
float
atdouble
kinakailangang gumamit ng mga paraan ng paghahambing ng kaukulang mga klase ng wrapperFloat.compare()
atDouble.compare()
- para sa mga primitive na uri (maliban
- At panghuli, sagutin ang tatlong tanong: simetriko ba ang ipinatupad na paraan ? Palipat ? Sumang-ayon ? Ang iba pang dalawang prinsipyo ( reflexivity at katiyakan ) ay karaniwang awtomatikong isinasagawa.
Mga panuntunan sa pag-override ng HashCode
Ang hash ay isang numerong nabuo mula sa isang bagay na naglalarawan sa estado nito sa isang punto ng oras. Ang numerong ito ay ginagamit sa Java pangunahin sa mga hash table gaya ngHashMap
. Sa kasong ito, ang hash function ng pagkuha ng isang numero batay sa isang bagay ay dapat ipatupad sa paraang masiguro ang isang medyo pantay na pamamahagi ng mga elemento sa hash table. At upang mabawasan din ang posibilidad ng mga banggaan kapag ang function ay nagbabalik ng parehong halaga para sa iba't ibang mga susi.
HashCode ng kontrata
Upang magpatupad ng hash function, tinutukoy ng detalye ng wika ang mga sumusunod na panuntunan:- Ang pagtawag sa isang pamamaraan
hashCode
ng isa o higit pang beses sa parehong bagay ay dapat magbalik ng parehong halaga ng hash, sa kondisyon na ang mga patlang ng bagay na kasangkot sa pagkalkula ng halaga ay hindi nagbago. - Ang pagtawag sa isang pamamaraan
hashCode
sa dalawang bagay ay dapat palaging ibalik ang parehong numero kung ang mga bagay ay pantay (ang pagtawag sa isang paraanequals
sa mga bagay na ito ay nagbabaliktrue
). - Ang pagtawag sa isang pamamaraan
hashCode
sa dalawang hindi pantay na bagay ay dapat magbalik ng magkaibang mga halaga ng hash. Bagama't hindi sapilitan ang pangangailangang ito, dapat isaalang-alang na ang pagpapatupad nito ay magkakaroon ng positibong epekto sa pagganap ng mga hash table.
Ang mga katumbas at hashCode na pamamaraan ay dapat na ma-override nang magkasama
Batay sa mga kontratang inilarawan sa itaas, sumusunod na kapag na-override ang pamamaraan sa iyong codeequals
, dapat mong palaging i-override ang pamamaraan hashCode
. Dahil sa katunayan ang dalawang pagkakataon ng isang klase ay magkaiba dahil sila ay nasa magkaibang lugar ng memorya, kailangan nilang ikumpara ayon sa ilang lohikal na pamantayan. Alinsunod dito, dapat ibalik ng dalawang lohikal na katumbas na bagay ang parehong halaga ng hash. Ano ang mangyayari kung isa lamang sa mga pamamaraang ito ang ma-override?
-
equals
OohashCode
hindiSabihin nating tama naming tinukoy ang isang pamamaraan
equals
sa aming klase, athashCode
nagpasya na iwanan ang pamamaraan na ito ay nasa klaseObject
. Pagkatapos mula sa punto ng view ng pamamaraanequals
ang dalawang bagay ay magiging lohikal na pantay, habang mula sa punto ng view ng pamamaraanhashCode
ay wala silang magkakatulad. At sa gayon, sa pamamagitan ng paglalagay ng isang bagay sa isang hash table, nagkakaroon tayo ng panganib na hindi ito maibalik sa pamamagitan ng key.
Halimbawa, tulad nito:Map<Point, String> m = new HashMap<>(); m.put(new Point(1, 1), “Point A”); // pointName == null String pointName = m.get(new Point(1, 1));
Malinaw, ang bagay na inilalagay at ang bagay na hinahanap ay dalawang magkaibang bagay, bagama't sila ay lohikal na pantay. Pero, kasi mayroon silang iba't ibang mga halaga ng hash dahil nilabag namin ang kontrata, maaari naming sabihin na nawala namin ang aming bagay sa isang lugar sa bituka ng hash table.
-
hashCode
Ooequals
hindi.Ano ang mangyayari kung i-override natin ang pamamaraan
hashCode
atequals
mamanahin ang pagpapatupad ng pamamaraan mula sa klaseObject
. Tulad ng alam mo, angequals
default na paraan ay naghahambing lamang ng mga pointer sa mga bagay, na tinutukoy kung ang mga ito ay tumutukoy sa parehong bagay. Ipagpalagay natin nahashCode
isinulat natin ang pamamaraan ayon sa lahat ng mga canon, ibig sabihin, nabuo ito gamit ang IDE, at ibabalik nito ang parehong mga halaga ng hash para sa lohikal na magkaparehong mga bagay. Malinaw, sa pamamagitan ng paggawa nito natukoy na namin ang ilang mekanismo para sa paghahambing ng dalawang bagay.Samakatuwid, ang halimbawa mula sa nakaraang talata ay dapat isagawa sa teorya. Ngunit hindi pa rin namin mahahanap ang aming bagay sa hash table. Bagaman magiging malapit tayo dito, dahil sa pinakamababa ay makakahanap tayo ng hash table basket kung saan ang bagay ay magsisinungaling.
Upang matagumpay na maghanap ng isang bagay sa isang talahanayan ng hash, bilang karagdagan sa paghahambing ng mga halaga ng hash ng susi, ginagamit din ang pagpapasiya ng lohikal na pagkakapantay-pantay ng susi sa hinanap na bagay. Iyon ay,
equals
walang paraan upang gawin nang walang overriding ang pamamaraan.
Pangkalahatang algorithm para sa pagtukoy ng hashCode
Dito, tila sa akin, hindi ka dapat mag-alala nang labis at bumuo ng pamamaraan sa iyong paboritong IDE. Dahil ang lahat ng mga paglilipat ng mga piraso sa kanan at kaliwa sa paghahanap ng gintong ratio, ibig sabihin, normal na pamamahagi - ito ay para sa ganap na matigas ang ulo dudes. Sa personal, duda ako na magagawa ko nang mas mahusay at mas mabilis kaysa sa parehong Ideya.Sa halip na isang konklusyon
Kaya, nakikita namin na ang mga pamamaraanequals
ay gumaganap hashCode
ng isang mahusay na tinukoy na papel sa wikang Java at idinisenyo upang makuha ang lohikal na pagkakapantay-pantay na katangian ng dalawang bagay. Sa kaso ng pamamaraan, equals
ito ay may direktang kaugnayan sa paghahambing ng mga bagay, sa kaso ng hashCode
hindi direktang isa, kapag kinakailangan, sabihin nating, upang matukoy ang tinatayang lokasyon ng isang bagay sa mga hash table o katulad na mga istruktura ng data upang dagdagan ang bilis ng paghahanap ng isang bagay. Bilang karagdagan sa mga kontrata , equals
may hashCode
isa pang kinakailangan na nauugnay sa paghahambing ng mga bagay. Ito ang pagkakapare-pareho ng isang paraan compareTo
ng interface Comparable
na may isang equals
. Ang kinakailangang ito ay nag-oobliga sa developer na palaging bumalik x.equals(y) == true
kapag x.compareTo(y) == 0
. Iyon ay, nakikita natin na ang lohikal na paghahambing ng dalawang bagay ay hindi dapat sumalungat saanman sa aplikasyon at dapat palaging pare-pareho.
GO TO FULL VERSION