Kokias problemas reikėtų apsvarstyti, iš naujo apibrėžiant bendraamžius ir „hashCode“ „Java“?

Kokias problemas / klaidas reikėtų atsižvelgti, kai hashCode ir hashCode ?

617
26 авг. nustatė Matt Sheppard 26 rug. 2008-08-26 11:50 '08, 11:50, 2008-08-26 11:50
@ 11 atsakymų

Teorija (kalbų advokatams ir matematiniu požiūriu):

equals() ( javadoc ) turi apibrėžti lygiavertiškumo santykį (jis turi būti refleksyvus, simetriškas ir tranzitinis). Be to, jis turi būti nuoseklus (jei objektai nekeičiami, tada jis turi ir toliau grąžinti tą pačią vertę). Be to, o.equals(null) visada turėtų grąžinti o.equals(null) .

hashCode() ( javadoc ) taip pat turi būti nuoseklus (jei objektas nekeičia equals() , jis turi ir toliau grąžinti tą pačią vertę).

ryšys tarp dviejų metodų:

Kai a.equals(b) , tada a.hashCode() turi būti toks pat kaip b.hashCode() .

Praktiškai:

Jei nepaisysite vieno, turite pakeisti kitą.

Naudokite tuos pačius laukus, kuriuos naudojate apskaičiuojant equals() kad apskaičiuotumėte hashCode() .

Naudokite puikias pagalbininkų klases „ EqualsBuilder“ ir „ HashCodeBuilder“ iš „ Apache Commons > bibliotekos. Pavyzdys:

 public class Person { private String name; private int age; // ... @Override public int hashCode() { return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers // if deriving: appendSuper(super.hashCode()). append(name). append(age). toHashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof Person)) return false; if (obj == this) return true; Person rhs = (Person) obj; return new EqualsBuilder(). // if deriving: appendSuper(super.equals(obj)). append(name, rhs.name). append(age, rhs.age). isEquals(); } } 

Taip pat atminkite:

Jei naudojate rinkimo arba žemėlapio maišos funkciją, pvz., „ HashSet“ , „ LinkedHashSet“ , „ HashMap“ , „ Hashtable“ arba „ WeakHashMap“ , įsitikinkite, kad pagrindinių rinkinių , kuriuos įdėjote į kolekciją , hashCode () nesikeičia, kol objektas yra kolekcijoje. Neperšaunamas būdas užtikrinti, kad raktai yra nekintami, o tai turi ir kitų privalumų .

1390
26 авг. Antti Kissaniemi atsakymas 26 rug . 2008-08-26 12:12 '08 12:12 val. 2008-08-26 12:12

Yra keletas dalykų, kuriuos verta paminėti, jei susiduriate su klasėmis, kurios išlieka naudojant „Object-Relationship Mapper“ (ORM), pavyzdžiui, užmigdymo režimą, jei manote, kad tai jau buvo be reikalo sudėtinga!

Lazy pakrauti objektai yra poklasiai.

Jei jūsų objektai yra išsaugoti naudojant ORM, daugeliu atvejų jūs susidursite su dinaminiais proxy, kad nebūtų per anksti įkeltas objektas iš duomenų saugyklos. Šie prokurorai yra įgyvendinami kaip jūsų klasės poklasiai. Tai reiškia, kad this.getClass() == o.getClass() grąžina false . Pavyzdžiui:

 Person saved = new Person("John Doe"); Long key = dao.save(saved); dao.flush(); Person retrieved = dao.retrieve(key); saved.getClass().equals(retrieved.getClass()); // Will return false if Person is loaded lazy 

Jei dirbate su „ORM“, „ o instanceof Person yra vienintelis dalykas, kuris elgsis teisingai.

Lazy pakrauti objektai turi nulinius laukus.

ORM paprastai naudoja „getters“, kad priverstų pakrauti tingus įkeltus objektus. Tai reiškia, kad person.name bus null jei person pakrauna iš tingumo, net jei person.getName() prievarta apkrova ir grąžina „John Doe“. Mano patirtis rodo, kad tai dažniau pasitaiko hashCode() ir equals() .

Jei dirbate su ORM, būtinai naudokite „getters“ ir niekada hashCode() nuorodų „ hashCode() ir equals() .

Objekto išsaugojimas pakeis jo būseną.

Nuolatiniai objektai dažnai naudoja id lauką, kad išsaugotų objekto raktą. Šis laukas bus automatiškai atnaujinamas pirmą kartą įrašant objektą. Nenaudokite ID lauko hashCode() . Bet jūs galite jį naudoti equals() .

Dažnai naudoju šabloną

 if (this.getId() == null) { return this == other; } else { return this.getId().equals(other.getId()); } 

Bet: jūs negalite įtraukti getId() į hashCode() . Jei tai padarysite, kai objektas išsaugomas, pasikeičia jo hashCode . Jei objektas yra „ HashSet , niekada „nerasite“.

Mano pavyzdiniame person tikriausiai naudoju getName()getName() ir getName() plius getName() (tik getName() equals() . Tai yra normalu, jei kyla pavojus, kad „ hashCode() susiduria su „susidūrimais“, bet niekada nebus equals() .

hashCode() turi naudoti nepakeistą savybių poaibį iš equals()

282
02 нояб. Johannes Brodwall atsakymas lapkričio 2 d. 2008-11-02 05:58 '08 at 5:58 am 2008-11-02 05:58

obj.getClass() != getClass() .

Šis teiginys yra netikslinio equals() paveldėjimo equals() . JLS (Java kalbos specifikacija) rodo, kad jei A.equals(B) == true , tada B.equals(A) taip pat turėtų grįžti į true . Jei praleisite šį pareiškimą, kuris paveldi klases, kurios nepaiso equals() (ir keisti jo elgesį), tai pažeidžia šią specifikaciją.

Apsvarstykite šį pavyzdį, kas atsitinka, kai operatorius praleidžiamas:

  class A { int field1; A(int field1) { this.field1 = field1; } public boolean equals(Object other) { return (other != null  other instanceof A  ((A) other).field1 == field1); } } class B extends A { int field2; B(int field1, int field2) { super(field1); this.field2 = field2; } public boolean equals(Object other) { return (other != null  other instanceof B  ((B)other).field2 == field2  super.equals(other)); } } 

new A(1).equals(new A(1)) Be to, new B(1,1).equals(new B(1,1)) rezultatų rezultatas yra teisingas, kaip turėtų būti.

Atrodo labai gerai, bet pažiūrėkite, kas atsitiks, jei bandysime naudoti abi klases:

 A a = new A(1); B b = new B(1,1); a.equals(b) == true; b.equals(a) == false; 

Akivaizdu, kad tai neteisinga.

Jei norite suteikti simetrišką būklę. a = b, jei b = a ir Liskovo pakeitimo principas vadina super.equals(other) ne tik B , bet ir patikrina, pavyzdžiui, A :

 if (other instanceof B ) return (other != null  ((B)other).field2 == field2  super.equals(other)); if (other instanceof A) return super.equals(other); else return false; 

Bus rodoma:

 a.equals(b) == true; b.equals(a) == true; 

Jei, jei A nėra nuoroda į B , tai gali būti nuoroda į A klasę (nes jūs ją super.equals() ), tokiu atveju taip pat skambinate super.equals() .

82
28 авг. atsakymas pateikiamas Ran Biron 28 rug. 2008-08-28 16:16 '08, 16:16 pm 2008-08-28 16:16

Įgyvendinimui pagal paveldėjimą patikrinkite Tal Cohen sprendimą Kaip teisingai įgyvendinti lygiavertį () metodą?

Santrauka:

Joshua Bloch savo knygoje „Efektyvus Java programavimo kalbos vadovas“ (Addison-Wesley, 2001) teigia, kad „tiesiog nėra jokio būdo išplėsti tikrąją klasę ir pridėti aspektą, išlaikant vienodą sutartį“. Tal nesutinka.

Jo sprendimas yra įgyvendinti lygias (), sukeldamas kitas asimetrines aklasEquals () abiem kryptimis. blindlyEquals () yra iš naujo apibrėžtas poklasiais, lygus () yra paveldėtas ir niekada iš naujo apibrėžtas.

Pavyzdys:

 class Point { private int x; private int y; protected boolean blindlyEquals(Object o) { if (!(o instanceof Point)) return false; Point p = (Point)o; return (px == this.x  py == this.y); } public boolean equals(Object o) { return (this.blindlyEquals(o)  o.blindlyEquals(this)); } } class ColorPoint extends Point { private Color c; protected boolean blindlyEquals(Object o) { if (!(o instanceof ColorPoint)) return false; ColorPoint cp = (ColorPoint)o; return (super.blindlyEquals(cp)  cp.color == this.color); } } 

Atkreipkite dėmesį, kad lygus () turėtų dirbti per paveldėjimo hierarchijas, jei būtina laikytis Liskov pakeitimo principo .

43
11 сент. Kevino Wong atsakymas, rugsėjo 11 d 2008-09-11 06:06 '08, 6:06 am. 2008-09-11 06:06

Aš vis dar stebina, kad nė vienas iš jų nerekomendavo gvajavos bibliotekos.

  //Sample taken from a current working project of mine just to illustrate the idea @Override public int hashCode(){ return Objects.hashCode(this.getDate(), this.datePattern); } @Override public boolean equals(Object obj){ if ( ! obj instanceof DateAndPattern ) { return false; } return Objects.equal(((DateAndPattern)obj).getDate(), this.getDate())  Objects.equal(((DateAndPattern)obj).getDate(), this.getDatePattern()); } 
31
12 февр. atsakymą pateikė Eugenijus vasario 12 d. 2013-02-12 10:17 '13, 10:17, 2013-02-12 10:17

Superklasėje yra du būdai: java.>

 public boolean equals(Object obj) public int hashCode() 

Lygūs objektai turėtų sukurti tą patį maišos kodą, jei jie yra lygūs, bet nelygūs objektai neturėtų sukurti atskirų maišos kodų.

 public class Test { private int num; private String data; public boolean equals(Object obj) { if(this == obj) return true; if((obj == null) || (obj.getClass() != this.getClass())) return false; // object must be Test at this point Test test = (Test)obj; return num == test.num  (data == test.data || (data != null  data.equals(test.data))); } public int hashCode() { int hash = 7; hash = 31 * hash + num; hash = 31 * hash + (null == data ? 0 : data.hashCode()); return hash; } // other methods } 

Jei norite daugiau, patikrinkite šią nuorodą kaip http://www.javaranch.com/journal/2002/10/equalhash.html

Tai dar vienas pavyzdys, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html

Smagiai! @. @

26
20 дек. atsakymą pateikė Luna Kong 20 d. 2013-12-20 09:14 '13 ne 9:14 2013-12-20 09:14

Yra keletas būdų, kaip išbandyti lygiateisiškumą prieš tikrinant narių lygybę, ir manau, kad abi jos yra naudingos tinkamomis aplinkybėmis.

  • Naudokite operatoriaus instanceof .
  • Naudokite this.getClass().equals(that.getClass()) .

Galiu naudoti # 1 final lygiateisiškame įgyvendinime arba sąsajos diegime, kuris numato lygiaverčių algoritmą (pavyzdžiui, java.util rinkinio sąsajas, yra teisingas būdas patikrinti su (obj instanceof Set) arba bet kokia kita sąsaja, perskirstymas). Tai paprastai yra blogas pasirinkimas, kai lygiaverčius galima iš naujo apibrėžti, nes pažeidžia simetrijos nuosavybę.

2 variantas leidžia saugiai išplėsti klasę be vienodų ar lygių simetrijos laužymo.

Jei jūsų klasė taip pat yra Comparable , equals ir Comparable metodai taip pat turi būti nuoseklūs. Čia yra lygiavertės klasės šablonas Comparable klasėje:

 final class MyClass implements Comparable<MyClass> { … @Override public boolean equals(Object obj) {  if (!(obj instanceof MyClass)) return false; return compareTo((MyClass) obj) == 0; } } 
18
28 авг. atsakymas pateikiamas erickson 28 d. 2008-08-28 21:25 '08, 21:25, 2008-08-28 21:25

Dėl lygių žiūrėkite „ Equal Angelica > paslaptis . Man tai labai patinka. Ji taip pat labai dažnai užduoda klausimą apie „ Generics“ „Java“ . Čia žiūrėkite kitus savo straipsnius (slinkite žemyn į „Core Java“), kur ji ir toliau dirbs su 2 dalimi ir „mišraus tipo palyginimu“. Smagiai skaityti juos!

15
28 февр. Atsakymas pateikiamas Johannes Schaub - litb 28 vas . 2009-02-28 01:05 '09, 1:05 am. 2009-02-28 01:05

Lygių () metodas naudojamas dviejų objektų lygybei nustatyti.

kadangi int vertė yra 10, ji visada yra 10. Tačiau lygus () metodas yra lygus dviejų objektų lygybei. Kai sakome objektą, jis turės savybių. Siekiant spręsti lygybės klausimą, atsižvelgiama į šias savybes. Nereikia, kad būtų atsižvelgta į visas savybes, siekiant nustatyti lygybę, ir atsižvelgiant į klasės ir konteksto apibrėžimą, jis gali būti išspręstas. Tada lygus () metodas gali būti panaikintas.

visada ignoruojame hashCode () metodą, kai ignoruojame lygiavertį () metodą. Jei ne, kas atsitiks? Jei mūsų paraiškoje naudojame hashtables, jis neveiks taip, kaip tikėtasi. Kadangi hashCode naudojamas nustatytai reikšmių vertei nustatyti, ji neatšaukia teisingos atitinkamos rakto vertės.

Numatytasis įgyvendinimas, hashCode () metodas Objekto klasėje naudoja vidinį objekto adresą ir konvertuoja jį į sveikojo skaičiaus numerį ir grąžina jį.

 public class Tiger { private String color; private String stripePattern; private int height; @Override public boolean equals(Object object) { boolean result = false; if (object == null || object.getClass() != getClass()) { result = false; } else { Tiger tiger = (Tiger) object; if (this.color == tiger.getColor()  this.stripePattern == tiger.getStripePattern()) { result = true; } } return result; } // just omitted null checks @Override public int hashCode() { int hash = 3; hash = 7 * hash + this.color.hashCode(); hash = 7 * hash + this.stripePattern.hashCode(); return hash; } public static void main(String args[]) { Tiger bengalTiger1 = new Tiger("Yellow", "Dense", 3); Tiger bengalTiger2 = new Tiger("Yellow", "Dense", 2); Tiger siberianTiger = new Tiger("White", "Sparse", 4); System.out.println("bengalTiger1 and bengalTiger2: " + bengalTiger1.equals(bengalTiger2)); System.out.println("bengalTiger1 and siberianTiger: " + bengalTiger1.equals(siberianTiger)); System.out.println("bengalTiger1 hashCode: " + bengalTiger1.hashCode()); System.out.println("bengalTiger2 hashCode: " + bengalTiger2.hashCode()); System.out.println("siberianTiger hashCode: " + siberianTiger.hashCode()); } public String getColor() { return color; } public String getStripePattern() { return stripePattern; } public Tiger(String color, String stripePattern, int height) { this.color = color; this.stripePattern = stripePattern; this.height = height; } } 

Kodo išvesties pavyzdys:

 bengalTiger1 and bengalTiger2: true bengalTiger1 and siberianTiger: false bengalTiger1 hashCode: 1398212510 bengalTiger2 hashCode: 1398212510 siberianTiger hashCode: –1227465966 
11
24 окт. atsakymas pateikiamas rohan kamat 24 oct. 2013-10-24 13:56 '13, 13:56, 2013-10-24 13:56

Logiškai, mes turime:

a.getClass().equals(b.getClass()) a.equals(b)a.hashCode() == b.hashCode()

Bet ne atvirkščiai!

7
24 марта '13 в 17:34 2013-03-24 17:34 atsakymas pateikiamas Khaled.K kovo 24 d. 13 val. 2013-03-24 17:34

Vienas klausimas, kurį radau, buvo toks: kai du objektai turi nuorodas į vienas kitą (vienas pavyzdys yra tėvų ir vaikų santykis su tėvų patogumo metodu gauti visus vaikus).
Tokie dalykai yra gana dažni, pavyzdžiui, rodant sulaikytosios veiksenos režimą.

Jei į savo maišos kodą arba vienodus testus įtraukiate abu santykių galus, galite baigti rekursinį kilpą, kuris baigiasi „StackOverflowException“ išimtimi.
Paprasčiausias sprendimas - įtraukti metodus į „getChildren“ kolekciją.

6
03 сент. Atsakymas, kurį pateikė Darren Greaves 03 Sep. 2008-09-03 00:06 '08 at 0:06 2008-09-03 00:06

Kiti klausimai apie žymenis arba užduoti klausimą