CodeGym /Blog Jawa /Acak /padha lan metode hashCode: praktik paling apik
John Squirrels
tingkat
San Francisco

padha lan metode hashCode: praktik paling apik

Diterbitake ing grup
Hi! Dina iki kita bakal ngomong babagan rong cara penting ing Jawa: equals()lan hashCode(). Iki dudu sepisanan kita ketemu dheweke: kursus CodeGym diwiwiti kanthi pelajaran singkat babagan equals()— maca yen sampeyan lali utawa durung ndeleng sadurunge ... padha lan metode hashCode: praktik paling apik - 1Ing wulangan dina iki, kita bakal ngomong babagan konsep-konsep kasebut kanthi rinci. Lan pracaya kula, kita kudu ngomong bab! Nanging sadurunge kita pindhah menyang anyar, ayo kang refresh apa kita wis dijamin :) Nalika elinga, iku biasane idea ala kanggo mbandhingaké loro obyek nggunakake operator ==, amarga ==mbandhingaké referensi. Mangkene conto mobil saka pelajaran 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 misale jek kita wis digawe loro Carobyek podho rupo: Nilai saka kothak cocog loro obyek mobil padha, nanging asil comparison isih palsu. Kita wis ngerti alesan: car1lan car2referensi nuduhake alamat memori beda, supaya padha ora padha. Nanging kita isih pengin mbandhingake rong obyek kasebut, dudu rong referensi. Solusi paling apik kanggo mbandhingake obyek yaiku equals()metode.

metode equals().

Sampeyan bisa uga kelingan yen kita ora nggawe cara iki saka ngeruk, nanging kita override iku: equals()cara ditetepake ing Objectkelas. Sing jarene, ing wangun sing biasa, ora ana gunane:

public boolean equals(Object obj) {
   return (this == obj);
}
Iki carane equals()cara ditetepake ing Objectkelas. Iki minangka perbandingan referensi maneh. Yagene dheweke nggawe kaya ngono? Inggih, kados pundi pangripta basa mangertos obyek endi ing program sampeyan sing dianggep padha lan sing ora? :) Iki minangka titik utama metode equals()- sing nggawe kelas yaiku sing nemtokake karakteristik sing digunakake nalika mriksa kesetaraan obyek kelas. Banjur sampeyan ngganti equals()metode ing kelas sampeyan. Yen sampeyan ora ngerti artine "nemtokake karakteristik", ayo nimbang conto. Punika kelas prasaja makili 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.
}
Upaminipun kita lagi nulis program sing perlu kanggo nemtokake apa wong loro iku kembar identik utawa mung lookalikes. Kita duwe limang ciri: ukuran irung, werna mripat, gaya rambut, anané bekas, lan asil tes DNA (kanggo kesederhanaan, kita makili iki minangka kode integer). Apa ciri-ciri kasebut sing sampeyan pikir bakal ngidini program kita ngenali kembar sing padha? padha lan metode hashCode: praktik paling apik - 2Mesthi, mung tes DNA sing bisa menehi jaminan. Wong loro bisa duwe werna mripat padha, cukuran rambute, irung, lan malah bekas - ana akeh wong ing donya, lan iku mokal kanggo njamin sing ana ora doppelgängers metu ana. Nanging kita butuh mekanisme sing bisa dipercaya: mung asil tes DNA sing bakal nggawe kesimpulan sing akurat. Apa tegese iki kanggo equals()metode kita? We kudu override ingMankelas, njupuk menyang akun syarat program kita. Cara kasebut kudu mbandhingake int dnaCodelapangan saka rong obyek kasebut. Yen padha, banjur obyek padha.

@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Apa pancene prasaja? Ora temenan. We dilalekake soko. Kanggo obyek kita, kita nemtokake mung siji lapangan sing cocog kanggo netepake kesetaraan obyek: dnaCode. Saiki bayangake yen kita ora duwe 1, nanging 50 lapangan sing cocog. Lan yen kabeh 50 kolom saka rong obyek padha, banjur obyek padha. Skenario kasebut uga bisa ditindakake. Masalah utama yaiku nggawe kesetaraan kanthi mbandhingake 50 lapangan minangka proses sing mbutuhake wektu lan sumber daya. Saiki bayangake yen saliyane Mankelas kita, kita duwe Womankelas kanthi lapangan sing padha karo sing ana ing Man. Yen programmer liyane nggunakake kelas kita, dheweke bisa kanthi gampang nulis kode kaya iki:

public static void main(String[] args) {
  
   Man man = new Man(........); // A bunch of parameters in the constructor

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

   System.out.println(man.equals(woman));
}
Ing kasus iki, mriksa nilai lapangan ora ana gunane: kita bisa ndeleng manawa kita duwe obyek saka rong kelas sing beda, mula ora ana cara sing padha! Iki tegese kita kudu nambah mriksa kanggo equals()cara, mbandingaken kelas obyek dibandhingake. Iku apik sing kita panginten sing!

@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
Nanging mbok menawa kita wis lali bab liya? Hmm ... Minimal, kita kudu mriksa manawa kita ora mbandhingake obyek karo awake dhewe! Yen referensi A lan B nuduhake alamat memori padha, iku obyek padha, lan kita ora perlu sampah wektu lan 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;
}
Iku uga ora babras kanggo nambah mriksa kanggo null: ora obyek bisa padha karo null. Dadi, yen parameter metode nol, mula ora ana gunane mriksa tambahan. Kanthi kabeh iki, equals()cara kita kanggo Mankelas 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. Ing pungkasan dina, yen:
  • kita mbandhingake rong obyek saka kelas sing padha
  • lan obyek dibandhingake ora obyek padha
  • lan obyek liwati oranull
...banjur kita nerusake kanggo mbandhingake karakteristik sing cocog. Kanggo kita, iki tegese dnaCodelapangan saka rong obyek. Nalika ngatasi equals()metode kasebut, priksa manawa sampeyan kudu netepi syarat kasebut:
  1. Refleksivity.

    Nalika equals()cara digunakake kanggo mbandhingake obyek apa wae karo dhewe, iku kudu bali bener.
    Kita wis netepi syarat iki. Metode kita kalebu:

    
    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 b.equals(c)uga kudu bali bener.

  4. Ketekunan.

    Asil saka equals()kudu diganti mung nalika kothak melu diganti. Yen data saka rong obyek ora owah, banjur asil equals()kudu tansah padha.

  5. Ketimpangan karo null.

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

metode hashCode().

Saiki ayo ngomong babagan hashCode()metode kasebut. Kenapa perlu? Kanggo persis padha waé - kanggo mbandhingaké obyek. Nanging kita wis duwe equals()! Kenapa metode liyane? Jawaban iki prasaja: kanggo nambah kinerja. Fungsi hash, sing diwakili ing Jawa nggunakake hashCode()metode kasebut, ngasilake nilai numerik sing dawane tetep kanggo obyek apa wae. Ing Jawa, hashCode()cara kasebut ngasilake angka 32-bit ( int) kanggo obyek apa wae. Mbandhingake rong angka luwih cepet tinimbang mbandhingake rong obyek nggunakake equals()metode kasebut, utamane yen metode kasebut nganggep akeh lapangan. Yen program kita mbandhingake obyek, iki luwih gampang ditindakake kanthi nggunakake kode hash. Mung yen obyek padha adhedhasar hashCode()cara, comparison nerusake menyangequals()cara. Miturut cara, iki carane struktur data basis hash bisa digunakake, contone, akrab HashMap! Cara hashCode(), kaya equals()metode, ditindhes dening pangembang. Lan kaya equals(), hashCode()cara kasebut nduweni syarat resmi sing ditulis ing dokumentasi Oracle:
  1. Yen rong obyek padha (yaiku equals()cara ngasilake bener), mula kudu duwe kode hash sing padha.

    Yen ora, cara kita bakal ora ana gunane. Kaya sing wis kasebut ing ndhuwur, hashCode()priksa kudu luwih dhisik kanggo nambah kinerja. Yen kode hash beda-beda, priksa bakal bali palsu, sanajan obyek kasebut bener-bener padha miturut cara kita nemtokake equals()metode kasebut.

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

  3. Aturan 1 ora bisa digunakake ing arah ngelawan. Loro obyek sing beda bisa duwe kode hash sing padha.

Aturan katelu rada bingung. Kepiye carane bisa? Panjelasan cukup prasaja. Cara hashCode()ngasilake a int. An intminangka nomer 32-bit. Wis sawetara winates saka nilai: saka -2.147.483.648 kanggo +2.147.483.647. Ing tembung liyane, ana mung luwih saka 4 milyar nilai bisa kanggo int. Saiki bayangake sampeyan nggawe program kanggo nyimpen data babagan kabeh wong sing manggon ing Bumi. Saben wong bakal cocog karo obyek dhewe Person(padha karo Mankelas). Ana ~ 7,5 milyar wong sing manggon ing planet iki. Ing tembung liyane, ora ketompo carane pinter algoritma kita nulis kanggo ngowahiPersonobyek menyang int, kita mung ora duwe nomer bisa cukup. Kita mung duwe 4,5 milyar nilai int, nanging ana luwih akeh wong saka iku. Iki tegese ora ketompo carane hard kita nyoba, sawetara wong beda bakal duwe kode hash padha. Nalika iki kedadeyan (kode hash pas kanggo rong obyek sing beda) kita nyebat tabrakan. Nalika ngatasi hashCode()cara kasebut, salah sawijining tujuan programer yaiku nyuda jumlah tabrakan potensial. Accounting kanggo kabeh aturan iki, apa hashCode()cara bakal katon kaya ing Personkelas? Kaya iki:

@Override
public int hashCode() {
   return dnaCode;
}
kaget? :) Yen katon ing syarat, sampeyan bakal weruh sing kita tundhuk karo kabeh. Obyek sing equals()cara kita ngasilake bener uga bakal padha miturut hashCode(). Yen Personobyek kita loro padha ing equals(yaiku, padha duwe dnaCode), banjur cara kita ngasilake nomer sing padha. Ayo dipikirake conto sing luwih angel. Upamane program kita kudu milih mobil mewah kanggo kolektor mobil. Nglumpukake bisa dadi hobi sing rumit kanthi macem-macem keanehan. Mobil 1963 tartamtu bisa regane 100 kaping luwih saka mobil 1964. Mobil abang 1970 bisa regane 100 kaping luwih saka mobil biru saka merek sing padha ing taun sing padha. padha lan metode hashCode: praktik paling apik - 4Ing conto sadurunge, karo Personkelas, kita mbuwang akeh lapangan (yaiku karakteristik manungsa) minangka ora penting lan mung nggunakakednaCodelapangan ing comparison. Saiki kita kerja ing alam sing unik banget, sing ora ana rincian sing ora penting! Iki LuxuryAutokelas kita:

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.
}
Saiki kita kudu nimbang kabeh lapangan ing comparison kita. Kesalahan apa wae bisa biaya klien atusan ewu dolar, dadi luwih apik kanggo aman banget:

@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 wis ora lali kabeh mriksa kita ngomong bab sadurungé. Nanging saiki kita mbandhingake saben telung lapangan obyek kita. Kanggo program iki, kita butuh kesetaraan mutlak, yaiku kesetaraan saben lapangan. Apa bab hashCode?

@Override
public int hashCode() {
   int result = model == null ? 0 : model.hashCode();
   result = result + manufactureYear;
   result = result + dollarPrice;
   return result;
}
Lapangan modeling kelas kita yaiku String. Iki trep, amarga Stringkelas wis overrides hashCode()cara. Kita ngetung modelkode hash lapangan banjur nambahake jumlah rong kolom numerik liyane. Pangembang Jawa nduweni trik prasaja sing digunakake kanggo nyuda jumlah tabrakan: nalika ngitung kode hash, tikelake asil penengah kanthi prima ganjil. Nomer sing paling umum digunakake yaiku 29 utawa 31. Saiki kita ora bakal nyelidiki subtleties matematika, nanging ing mangsa ngarep, elinga yen nikelake asil penengah kanthi nomer ganjil sing cukup gedhe mbantu "nyebar" asil fungsi hash lan. akibate, ngurangi jumlah obyek karo kode hash 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 seluk-beluk mekanisme iki ing kirim iki ing StackOverflow , uga ing buku Jawa Efektif dening Joshua Bloch. Pungkasan, siji poin sing luwih penting sing kudu dicritakake. Saben-saben kita overrode equals()lan hashCode()cara, kita milih lapangan conto tartamtu sing dijupuk menyang akun ing cara iki. Cara kasebut nganggep lapangan sing padha. Nanging kita bisa nimbang lapangan beda 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;
}
Punika kita equals()lan hashCode()cara kanggo LuxuryAutokelas. Cara kasebut hashCode()tetep ora diganti, nanging kita mbusak modellapangan saka equals()metode kasebut. Model ora dadi ciri sing digunakake nalika equals()metode mbandhingake rong obyek. Nanging nalika ngetung kode hash, lapangan kasebut isih dianggep. Apa sing kita entuk minangka asil? Ayo nggawe rong mobil lan temokake!

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());
   }
}

Are these two objects equal to each other? 
true 
What are their hash codes? 
-1372326051 
1668702472
Kesalahan! Kanthi nggunakake lapangan beda kanggo equals()lan hashCode()cara, kita nerak kontrak sing wis ditetepake kanggo wong-wong mau! Loro obyek sing padha miturut equals()metode kasebut kudu duwe kode hash sing padha. Kita nampa nilai beda kanggo wong-wong mau. Kesalahan kasebut bisa nyebabake akibat sing ora bisa dipercaya, utamane nalika nggarap koleksi sing nggunakake hash. Akibaté, nalika sampeyan override equals()lan hashCode(), sampeyan kudu nimbang kolom padha. Pawulangan iki rada suwe, nanging sampeyan sinau akeh banget dina iki! :) Saiki wayahe bali kanggo ngrampungake tugas!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION