JavaRush /Blog Jawa /Random-JV /padha & cara hashCode: laku nggunakake

padha & cara hashCode: laku nggunakake

Diterbitake ing grup
Hello! Dina iki kita bakal ngomong babagan rong cara penting ing Jawa - equals()lan hashCode(). Iki dudu sepisanan kita ketemu karo dheweke: ing wiwitan kursus JavaRush ana ceramah singkat babagan - equals()maca yen sampeyan wis lali utawa durung weruh sadurunge. Cara padha & amp;  Kode hash: praktik panggunaan - 1Ing pawulangan dina iki kita bakal ngomong babagan konsep-konsep kasebut kanthi rinci - pracaya marang aku, ana akeh sing kudu dirembug! Lan sadurunge pindhah menyang sing anyar, ayo refresh memori babagan apa sing wis kita bahas :) Nalika sampeyan ngelingi, perbandingan biasa saka rong obyek nggunakake operator " ==" minangka ide sing ala, amarga " ==" mbandhingake referensi. Mangkene conto mobil saka kuliah anyar:
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
Output konsol:

false
Iku bakal koyone sing kita wis digawe loro obyek podho rupo saka kelas Car: kabeh lapangan ing loro mesin padha, nanging asil saka comparison isih palsu. Kita wis ngerti alesan: pranala car1lan car2nuding alamat beda ing memori, supaya padha ora padha. Kita isih pengin mbandhingake rong obyek, dudu rong referensi. Solusi paling apik kanggo mbandhingake obyek yaiku equals().

metode equals().

Sampeyan bisa uga ngelingi yen kita ora nggawe cara iki saka ngeruk, nanging override - sawise kabeh, cara equals()ditetepake ing kelas Object. Nanging, ing wangun biasanipun, ora ana gunane:
public boolean equals(Object obj) {
   return (this == obj);
}
Iki carane metode equals()ditetepake ing kelas Object. Perbandingan pranala sing padha. Kenapa dheweke digawe kaya iki? Inggih, kepiye para pangripta basa ngerti apa obyek ing program sampeyan dianggep padha lan sing ora? :) Iki minangka ide utama metode equals()- panyipta kelas kasebut dhewe nemtokake karakteristik sing dicenthang kesetaraan obyek kelas iki. Kanthi nindakake iki, sampeyan ngilangi metode equals()ing kelas sampeyan. Yen sampeyan ora ngerti artine "sampeyan nemtokake karakteristik dhewe," ayo goleki conto. Punika kelas prasaja saka wong - Man.
public class Man {

   private String noseSize;
   private String eyesColor;
   private String haircut;
   private boolean scars;
   private int dnaCode;

public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
   this.noseSize = noseSize;
   this.eyesColor = eyesColor;
   this.haircut = haircut;
   this.scars = scars;
   this.dnaCode = dnaCode;
}

   //getters, setters, etc.
}
Ayo dadi ngomong kita nulis program sing perlu kanggo nemtokake apa wong loro related dening kembar, utawa mung doppelgängers. Kita duwe limang ciri: ukuran irung, warna mata, gaya rambut, anané bekas luka lan asil tes biologi DNA (kanggo kesederhanaan - ing wangun nomer kode). Endi saka ciri-ciri kasebut sampeyan mikir bakal ngidini program kita ngenali sederek kembar? Cara padha & amp;  kode hash: praktik panggunaan - 2Mesthine, mung tes biologi sing bisa menehi jaminan. Wong loro bisa duwe werna mripat sing padha, gaya rambut, irung, lan malah bekas - ana akeh wong ing donya, lan ora mungkin kanggo ngindhari kebetulan. Kita butuh mekanisme sing bisa dipercaya: mung asil tes DNA sing ngidini kita nggawe kesimpulan sing akurat. Apa tegese iki kanggo metode kita equals()? We kudu redefine ing kelas Mannjupuk menyang akun syarat program kita. Cara kasebut kudu mbandhingake lapangan int dnaCoderong obyek, lan yen padha, mula obyek kasebut padha.
@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Apa pancene prasaja? Ora temenan. Kita ora kejawab soko. Ing kasus iki, kanggo obyek kita wis ditetepake mung siji "pinunjul" lapangan kang podo ditetepake - dnaCode. Saiki bayangake yen kita ora duwe 1, nanging 50 kolom "penting" kasebut. Lan yen kabeh 50 kolom saka rong obyek padha, mula obyek kasebut padha. Iki uga bisa kedadeyan. Masalah utama yaiku ngitung kesetaraan 50 lapangan minangka proses sing akeh wektu lan sumber daya. Saiki bayangake yen saliyane kelas, Mankita duwe kelas Womankanthi lapangan sing padha kaya ing Man. Lan yen programer liyane nggunakake kelas sampeyan, dheweke bisa kanthi gampang nulis ing program kaya:
public static void main(String[] args) {

   Man man = new Man(........); //a bunch of parameters in the constructor

   Woman woman = new Woman(.........);//same bunch of parameters.

   System.out.println(man.equals(woman));
}
Ing kasus iki, ora ana gunane kanggo mriksa nilai lapangan: kita ndeleng manawa kita ndeleng obyek saka rong kelas sing beda, lan ora bisa padha ing prinsip! Iki tegese kita kudu mriksa metode kasebut equals()- mbandhingake obyek saka rong kelas sing padha. Iku apik yen kita mikir iki!
@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Nanging mungkin kita lali bab liya? Hmm ... Minimal, kita kudu mriksa manawa kita ora mbandhingake obyek kasebut karo awake dhewe! Yen referensi A lan B nuduhake alamat sing padha ing memori, banjur padha obyek padha, lan kita uga ora perlu sampah wektu mbandhingaké 50 lapangan.
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Kajaba iku, iku ora babras kanggo nambah mriksa kanggo null: ora obyek bisa padha karo null, kang cilik ora ana titik ing mriksa tambahan. Nganggep kabeh iki, metode equals()kelas kita Manbakal katon kaya iki:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Kita nindakake kabeh pamriksa awal sing kasebut ing ndhuwur. Yen ternyata:
  • kita mbandhingake rong obyek saka kelas sing padha
  • iki dudu obyek sing padha
  • kita ora mbandhingake obyek kita karonull
...banjur kita nerusake kanggo mbandhingake ciri sing signifikan. Ing kasus kita, lapangan dnaCodesaka rong obyek. Nalika ngatasi cara equals(), priksa manawa sampeyan netepi syarat kasebut:
  1. Refleksivity.

    Sembarang obyek kudu equals()dhewe.
    Kita wis njupuk syarat iki menyang akun. Cara kita nyatakake:

    if (this == o) return true;

  2. simetri.

    Yen a.equals(b) == true, banjur b.equals(a)kudu bali true.
    Cara kita uga nyukupi syarat kasebut.

  3. Transitivity.

    Yen rong obyek padha karo sawetara obyek katelu, mula kudu padha karo siji liyane.
    Yen a.equals(b) == truelan a.equals(c) == true, banjur mriksa b.equals(c)uga kudu bali bener.

  4. Langgeng.

    Asil karya equals()kudu diganti mung nalika lapangan sing kalebu ing owah-owahan. Yen data saka rong obyek ora owah, asil pamriksa equals()kudu padha.

  5. Ketimpangan karo null.

    Kanggo obyek apa wae, priksa a.equals(null)kudu bali palsu.
    Iki ora mung sawetara "rekomendasi sing migunani", nanging kontrak cara sing ketat , sing diwenehake ing dokumentasi Oracle

metode hashCode().

Saiki ayo ngomong babagan metode kasebut hashCode(). Kenapa perlu? Persis kanggo tujuan sing padha - mbandhingake obyek. Nanging kita wis duwe equals()! Kenapa metode liyane? Jawaban iki prasaja: kanggo nambah produktivitas. Fungsi hash, sing diwakili dening metode , ing Jawa hashCode(), ngasilake nilai numerik sing dawane tetep kanggo obyek apa wae. Ing kasus Jawa, cara kasebut hashCode()ngasilake nomer 32-bit saka jinis int. Mbandhingake rong angka kanthi siji liyane luwih cepet tinimbang mbandhingake rong obyek nggunakake metode kasebut equals(), utamane yen nggunakake akeh lapangan. Yen program kita bakal mbandhingake obyek, iku luwih gampang kanggo nindakake iki dening kode hash, lan mung yen padha karo hashCode()- nerusake kanggo comparison dening equals(). Iki, kanthi cara, kepiye struktur data adhedhasar hash bisa digunakake - contone, sing sampeyan ngerti HashMap! Cara hashCode(), kaya equals(), diganti dening pangembang dhewe. Lan kaya kanggo equals(), metode kasebut hashCode()nduweni syarat resmi sing ditemtokake ing dokumentasi Oracle:
  1. Yen rong obyek padha (yaiku, cara equals()ngasilake bener), kudu duwe kode hash sing padha.

    Yen ora, cara kita bakal ora ana gunane. Priksa dening hashCode(), minangka kita ngandika, kudu teka pisanan kanggo nambah kinerja. Yen kode hash beda-beda, mriksa bakal bali palsu, sanajan obyek sing bener padha (kaya kita ditetepake ing cara equals()).

  2. Yen cara hashCode()diarani kaping pirang-pirang ing obyek sing padha, mesthine kudu ngasilake nomer sing padha saben wektu.

  3. Aturan 1 ora bisa mbalikke. Loro obyek sing beda bisa duwe kode hash sing padha.

Aturan katelu rada bingung. Kepiye carane iki? Panjelasan cukup prasaja. Metode kasebut hashCode()bali int. intiku nomer 32-bit. Nduwe jumlah winates - saka -2,147,483,648 nganti +2,147,483,647. Ing tembung liyane, ana mung luwih saka 4 milyar variasi saka nomer int. Saiki bayangake sampeyan nggawe program kanggo nyimpen data babagan kabeh wong sing urip ing Bumi. Saben wong bakal duwe obyek kelas dhewe Man. ~ 7,5 milyar wong manggon ing bumi. Ing tembung liyane, ora ketompo carane apik algoritma Mankita nulis kanggo ngowahi obyek menyang nomer, kita mung ora bakal cukup nomer. Kita mung duwe 4,5 milyar opsi, lan akeh wong liyane. Iki tegese ora ketompo carane hard kita nyoba, kode hash bakal padha kanggo sawetara wong beda. Kahanan iki (kode hash saka rong obyek sing cocog) diarani tabrakan. Salah sawijining tujuan programmer nalika ngatasi metode hashCode()yaiku nyuda kemungkinan tabrakan. Apa cara kita hashCode()kanggo kelas katon kaya Man, njupuk menyang akun kabeh aturan iki? Kaya iki:
@Override
public int hashCode() {
   return dnaCode;
}
kaget? :) Ora dikarepke, nanging yen katon ing syarat, sampeyan bakal weruh sing kita tundhuk karo kabeh. Obyek sing equals()bali kita bener bakal padha ing hashCode(). Yen rong obyek kita Manpadha karo nilai equals(yaiku, padha duwe nilai sing padha dnaCode), metode kita bakal ngasilake nomer sing padha. Ayo goleki conto sing luwih rumit. Dadi program kita kudu milih mobil mewah kanggo klien kolektor. Nglumpukake minangka perkara sing rumit, lan ana akeh fitur. Mobil saka taun 1963 bisa regane 100 kaping luwih saka mobil sing padha wiwit taun 1964. Mobil abang saka taun 1970 bisa regane 100 kaping luwih saka mobil biru sing digawe ing taun sing padha. Cara padha & amp;  Kode hash: praktik panggunaan - 4Ing kasus sing sepisanan, karo kelas Man, kita mbuwang sebagian besar lapangan (yaiku, karakteristik wong) minangka ora pati penting lan mung digunakake lapangan kanggo mbandhingake dnaCode. Ing kene kita nggarap wilayah sing unik banget, lan ora bisa ana rincian cilik! Iki kelas kita LuxuryAuto:
public class LuxuryAuto {

   private String model;
   private int manufactureYear;
   private int dollarPrice;

   public LuxuryAuto(String model, int manufactureYear, int dollarPrice) {
       this.model = model;
       this.manufactureYear = manufactureYear;
       this.dollarPrice = dollarPrice;
   }

   //... getters, setters, etc.
}
Ing kene, nalika mbandhingake, kita kudu nganggep kabeh lapangan. Sembarang kesalahan bisa biaya atusan ewu dolar kanggo klien, supaya luwih aman:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;

   LuxuryAuto that = (LuxuryAuto) o;

   if (manufactureYear != that.manufactureYear) return false;
   if (dollarPrice != that.dollarPrice) return false;
   return model.equals(that.model);
}
Ing cara kita, equals()kita ora lali babagan kabeh mriksa sing kita ngomong sadurunge. Nanging saiki kita mbandhingake saben telung lapangan obyek kita. Ing program iki, kesetaraan kudu mutlak, ing saben lapangan. Pripun hashCode?
@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = result + manufactureYear;
   result = result + dollarPrice;
   return result;
}
Lapangan modeling kelas kita minangka string. Iki trep: Stringcara hashCode()wis ditindhes ing kelas. Kita ngetung kode hash lapangan model, lan nambahake jumlah rong kolom numerik liyane. Ana trik cilik ing Jawa sing digunakake kanggo nyuda jumlah tabrakan: nalika ngetung kode hash, tikelake asil penengah kanthi nomer prima ganjil. Nomer sing paling umum digunakake yaiku 29 utawa 31. Saiki kita ora bakal njlèntrèhaké rincian matématika, nanging kanggo referensi ing mangsa ngarep, elinga yen nikelake asil penengah kanthi nomer ganjil sing cukup gedhe mbantu "nyebar" asil hash. fungsi lan mungkasi karo obyek kurang karo hashcode padha. Kanggo metode kita hashCode()ing LuxuryAuto bakal katon kaya iki:
@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = 31 * result + manufactureYear;
   result = 31 * result + dollarPrice;
   return result;
}
Sampeyan bisa maca liyane babagan kabeh intricacies mekanisme iki ing kirim iki ing StackOverflow , uga ing buku Joshua Bloch " Jawa Efektif ". Pungkasan, ana siji titik sing luwih penting sing kudu dicritakake. Saben wektu nalika overriding equals(), hashCode()kita milih lapangan tartamtu saka obyek, kang dijupuk menyang akun ing cara iki. Nanging kita bisa njupuk menyang akun beda lapangan ing equals()lan hashCode()? Secara teknis, kita bisa. Nanging iki minangka gagasan sing ala, lan iki sebabe:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;

   LuxuryAuto that = (LuxuryAuto) o;

   if (manufactureYear != that.manufactureYear) return false;
   return dollarPrice == that.dollarPrice;
}

@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = 31 * result + manufactureYear;
   result = 31 * result + dollarPrice;
   return result;
}
Mangkene cara equals()kanggo hashCode()kelas LuxuryAuto. Cara kasebut hashCode()tetep ora diganti, lan equals()kita mbusak lapangan saka metode kasebut model. Saiki model ora dadi karakteristik kanggo mbandhingake rong obyek kanthi equals(). Nanging isih dianggep nalika ngitung kode hash. Apa sing bakal kita entuk minangka asil? Ayo nggawe rong mobil lan priksa!
public class Main {

   public static void main(String[] args) {

       LuxuryAuto ferrariGTO = new LuxuryAuto("Ferrari 250 GTO", 1963, 70000000);
       LuxuryAuto ferrariSpider = new LuxuryAuto("Ferrari 335 S Spider Scaglietti", 1963, 70000000);

       System.out.println("Are these two objects equal to each other?");
       System.out.println(ferrariGTO.equals(ferrariSpider));

       System.out.println("What are their hash codes?");
       System.out.println(ferrariGTO.hashCode());
       System.out.println(ferrariSpider.hashCode());
   }
}

Эти два an object равны друг другу?
true
Какие у них хэш-codeы?
-1372326051
1668702472
Kesalahan! Kanthi nggunakake lapangan beda kanggo equals()lan hashCode()kita nerak kontrak ditetepake kanggo wong-wong mau! Loro equals()obyek sing padha kudu duwe kode hash sing padha. Kita entuk makna sing beda kanggo dheweke. Kesalahan kasebut bisa nyebabake akibat sing paling luar biasa, utamane nalika nggarap koleksi sing nggunakake hash. Mulane, nalika redefining equals()lan hashCode()bakal bener nggunakake kolom padha. Kuliah kasebut dadi cukup suwe, nanging dina iki sampeyan sinau akeh perkara anyar! :) Iku wektu kanggo bali kanggo ngrampungake masalah!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION