Programmēšana

Java pamata hashCode un vienāds ar demonstrācijām

Man bieži patīk izmantot šo emuāru, lai atkārtoti apmeklētu grūti nopelnītās nodarbības Java pamatos. Šis emuāra ziņojums ir viens no šādiem piemēriem un koncentrējas uz bīstamo spēku ilustrāciju aiz metodēm vienāds (Object) un hashCode (). Es neaptvēršu visas šo divu ļoti nozīmīgo metožu nianses, kas visiem Java objektiem ir skaidri deklarēti vai netieši mantoti no vecākiem (iespējams, tieši no paša Object), bet es aplūkošu dažus no izplatītākajiem jautājumiem, kas rodas, kad tie ir nav ieviesti vai netiek pareizi ieviesti. Ar šīm demonstrācijām es arī mēģinu parādīt, kāpēc ir svarīgi veikt rūpīgu kodu pārskatīšanu, rūpīgu vienību testēšanu un / vai uz rīku balstītu analīzi, lai pārbaudītu šo metožu ieviešanas pareizību.

Tā kā visi Java objekti galu galā pārmanto ieviešanu vienāds (objekts) un hashCode (), Java kompilators un patiešām Java izpildlaika palaišanas programma neziņos par problēmām, atsaucoties uz šīm šo metožu "noklusējuma ieviešanām". Diemžēl, kad šīs metodes ir nepieciešamas, šo metožu noklusējuma ieviešana (piemēram, viņu brālēns toString metode) reti tiek vēlēta. Javadoc balstītā objektu klases API dokumentācijā ir apspriests "līgums", kas sagaidāms no jebkuras programmas ieviešanas vienāds (objekts) un hashCode () metodes, kā arī tiek apspriesta katra iespējamā noklusējuma ieviešana, ja to neievēro bērnu klases.

Piemēriem šajā ziņojumā es izmantoju klasi HashAndEquals, kuras kodu saraksts tiek parādīts blakus dažādu objektu personu objektu parādīšanai ar atšķirīgu atbalsta līmeni hashCode un ir vienāds metodes.

HashAndEquals.java

iepakojums dustin.piemēri; importēt java.util.HashSet; importēt java.util.Set; importēt statisko java.lang.System.out; publiskā klase HashAndEquals {private static final String HEADER_SEPARATOR = "========================================= =============================== "; privāta statiska gala int HEADER_SEPARATOR_LENGTH = HEADER_SEPARATOR.length (); privāta statiska galīgā virkne NEW_LINE = System.getProperty ("line.separator"); privāta galīgā persona persona1 = jauna persona ("Flintstone", "Fred"); privāta galīgā persona persona2 = jauna persona ("drupa", "Bārnijs"); privāta galīgā persona persona3 = jauna persona ("Flintstone", "Fred"); privāta galīgā persona persona4 = jauna persona ("drupa", "Bārnijs"); public void displayContents () {printHeader ("OBJEKTU SATURS"); out.println ("Persona 1:" + persona1); out.println ("Persona 2:" + persona2); out.println ("Persona 3:" + persona3); out.println ("Persona 4:" + persona4); } public void CompareEquality () {printHeader ("VIENLĪDZĪBAS SALĪDZINĀJUMI"); out.println ("Persona1.vienāda (Persona2):" + persona1.vienāda (persona2)); out.println ("Persona1.vienāda (Persona3):" + persona1.vienāda (persona3)); out.println ("Persona2.vienāda (Persona4):" + persona2.vienāda (persona4)); } public void CompareHashCodes () {printHeader ("SALĪDZINĀT HASH KODUS"); out.println ("Person1.hashCode ():" + personai1.hashCode ()); out.println ("Persona2.hashCode ():" + persona2.hashCode ()); out.println ("Person3.hashCode ():" + person3.hashCode ()); out.println ("Person4.hashCode ():" + person4.hashCode ()); } public Set addToHashSet () {printHeader ("PIEVIENOT IESTATAMOS ELEMENTUS - VAI PIEVIENOTI VAI VIENI?"); galīgais komplekts = jauns HashSet (); out.println ("Set.add (Persona1):" + set.add (Persona1)); out.println ("Set.add (Persona2):" + set.add (Persona2)); out.println ("Set.add (Persona3):" + set.add (persona3)); out.println ("Set.add (Person4):" + set.add (person4)); atgriešanās komplekts; } public void removeFromHashSet (final Set sourceSet) {printHeader ("NOŅEMT ELEMENTUS NO KOMPLEKTA - VAI ATTIECĪBĀ UZ TIKS NOZĪMĒT?"); out.println ("Set.remove (Person1):" + sourceSet.remove (person1)); out.println ("Set.remove (Person2):" + sourceSet.remove (person2)); out.println ("Set.remove (Person3):" + sourceSet.remove (person3)); out.println ("Set.remove (Person4):" + sourceSet.remove (person4)); } public static void printHeader (final String headerText) {out.println (NEW_LINE); out.println (HEADER_SEPARATOR); out.println ("=" + headerText); out.println (HEADER_SEPARATOR); } public static void main (final String [] argumenti) {final HashAndEquals instance = new HashAndEquals (); instance.displayContents (); instance.compareEquality (); instance.compareHashCodes (); gala komplekts = instance.addToHashSet (); out.println ("Iestatīt pirms noņemšanas:" + iestatīt); //instance.person1.setFirstName("Bam Bam "); instance.removeFromHashSet (kopa); out.println ("Iestatīt pēc noņemšanas:" + iestatīt); }} 

Iepriekš minētā klase tiks atkārtoti izmantota kā - ar vienu nelielu izmaiņu vēlāk amatā. Tomēr Persona klase tiks mainīta, lai atspoguļotu ir vienāds un hashCode un parādīt, cik viegli var tos sajaukt, vienlaikus grūti izsekot problēmai, ja ir kļūda.

Nav skaidra ir vienāds vai hashCode Metodes

Pirmā versija Persona klase nenodrošina nepārprotamu ignorētu versiju ir vienāds metode vai hashCode metodi. Tas parādīs katras no šīm mantotajām metodēm "noklusējuma ieviešanu" Objekts. Šeit ir koda avots Persona bez hashCode vai ir vienāds nepārprotami tiek ignorēts.

Person.java (nav izteikta hashCode vai vienāda ar metodi)

iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public String toString () {return this.firstName + "" + this.lastName; }} 

Šī pirmā versija Persona nenodrošina get / set metodes un nenodrošina ir vienāds vai hashCode ieviešana. Kad galvenā demonstrācijas klase HashAndEquals tiek izpildīts ar šī gadījuma gadījumiem ir vienāds-bez un hashCodebez Persona klasē, rezultāti parādās, kā parādīts nākamajā ekrāna momentuzņēmumā.

No iepriekš redzamās izejas var izdarīt vairākus novērojumus. Pirmkārt, bez skaidras ANO ieviešanas vienāds (objekts) metodi, neviens no Persona tiek uzskatīti par vienādiem, pat ja visi gadījumu (divu virkņu) atribūti ir identiski. Tas ir tāpēc, ka, kā paskaidrots Object.equals (Object) dokumentācijā, noklusējums ir vienāds ieviešanas pamatā ir precīza atsauces atbilstība:

Objekta klases vienāda metode īsteno visdiskriminējošāko iespējamo līdzvērtības attiecību ar objektiem; tas ir, visām nulles atsauces vērtībām x un y šī metode atgriež vērtību true un tikai tad, ja x un y attiecas uz vienu un to pašu objektu (x == y ir vērtība true).

Otrais novērojums no šī pirmā piemēra ir tāds, ka hash kods katram Persona objekts pat tad, ja diviem gadījumiem visiem to atribūtiem ir vienādas vērtības. HashSet atgriežas taisnība kad kopai tiek pievienots "unikāls" objekts (HashSet.add) vai nepatiesa ja pievienotais objekts netiek uzskatīts par unikālu un tāpēc netiek pievienots. Līdzīgi HashSetatgriež metodi noņemt taisnība ja sniegtais objekts tiek uzskatīts par atrastu un noņemtu vai nepatiesa ja tiek uzskatīts, ka norādītais objekts nav HashSet un tāpēc to nevar noņemt. Tāpēc ka ir vienāds un hashCode iedzimtas noklusējuma metodes izturas pret šiem gadījumiem kā pilnīgi atšķirīgus, nav pārsteigums, ka visi tiek pievienoti kopai un visi tiek veiksmīgi noņemti no kopas.

Skaidrs ir vienāds Tikai metode

Programmas otrā versija Persona klase ietver nepārprotami ignorētu ir vienāds metodi, kā parādīts nākamajā kodu sarakstā.

Person.java (paredzēta vienāda metode)

iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }} 

Kad gadījumi Persona ar vienāds (objekts) tiek izmantoti skaidri definēti, izeja ir tāda, kā parādīts nākamajā ekrāna momentuzņēmumā.

Pirmais novērojums ir tāds, ka tagad ir vienāds aicina Persona gadījumi patiešām atgriežas taisnība kad objekts ir vienāds, ja visi atribūti ir vienādi, nevis pārbauda stingru atsauces vienādību. Tas parāda, ka paradums ir vienāds ieviešana Persona ir paveicis savu darbu. Otrais novērojums ir tas, ka ir vienāds metode nav ietekmējusi spēju pievienot un noņemt šķietami to pašu objektu HashSet.

Skaidrs ir vienāds un hashCode Metodes

Ir pienācis laiks pievienot nepārprotamu hashCode () metodi Persona klasē. Patiešām, tas tiešām bija jādara, kad ir vienāds metode tika ieviesta. Iemesls tam ir norādīts programmas dokumentācijā Object.equals (Object) metode:

Ņemiet vērā, ka vienmēr ir jāignorē hashCode metode ikreiz, kad šī metode tiek ignorēta, lai saglabātu vispārējo hashCode metodes līgumu, kas nosaka, ka vienādiem objektiem jābūt vienādiem hash kodiem.

Šeit ir Persona ar skaidri īstenotu hashCode metode, kuras pamatā ir tie paši Personair vienāds metodi.

Person.java (tiešs vienāds ar hashCode ieviešanu)

iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public int hashCode () {return lastName.hashCode () + firstName.hashCode (); } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }} 

Rezultāts, darbojoties ar jauno Persona klase ar hashCode un ir vienāds metodes tiek parādītas tālāk.

Nav pārsteidzoši, ka hash kodi, kas atgriezti objektiem ar vienādām atribūtu vērtībām, tagad ir vienādi, taču interesantāks novērojums ir tas, ka mēs varam tikai divus no četriem gadījumiem pievienot HashSet tagad. Tas ir tāpēc, ka trešais un ceturtais pievienošanas mēģinājums tiek uzskatīts par mēģinājumu pievienot objektu, kas jau tika pievienots kopai. Tā kā bija tikai divi pievienoti, var atrast un noņemt tikai divus.

Problēmas ar maināmiem hashCode atribūtiem

Attiecībā uz ceturto un pēdējo piemēru šajā amatā es skatos, kas notiek, kad hashCode ieviešanas pamatā ir atribūts, kas mainās. Šajā piemērā: a setFirstName metode tiek pievienota Persona un galīgais modifikators tiek noņemts no tā vārds atribūts. Turklāt galvenajai HashAndEquals klasei ir jānoņem komentārs no rindas, kas izsauc šo jauno kopas metodi. Jaunā versija Persona tiek parādīts nākamais.

iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāta virkne firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public int hashCode () {return lastName.hashCode () + firstName.hashCode (); } public void setFirstName (galīgā virkne newFirstName) {this.firstName = newFirstName; } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }} 

Rezultāts, kas iegūts, izpildot šo piemēru, tiek parādīts nākamais.

$config[zx-auto] not found$config[zx-overlay] not found