Mengapa mengatasi kaedah equals dan hashcode dalam Java?
Sumber:
Sederhana Artikel ini memfokuskan kepada dua kaedah yang berkait rapat: equals() dan hashcode() . Anda akan belajar cara mereka berinteraksi antara satu sama lain dan cara mengatasinya dengan betul.
Mengapa kita mengatasi kaedah equals()?
Di Jawa, kita tidak boleh membebankan tingkah laku pengendali seperti
== ,
+= ,
-+ . Mereka bekerja mengikut proses tertentu. Sebagai contoh, pertimbangkan operasi operator
== .
Bagaimanakah pengendali == berfungsi?
Ia menyemak sama ada kedua-dua rujukan yang dibandingkan menunjuk kepada contoh yang sama dalam ingatan. Operator
== hanya akan menilai kepada benar jika kedua-dua rujukan mewakili contoh yang sama dalam ingatan. Mari kita lihat kod sampel:
public class Person {
private Integer age;
private String name;
..getters, setters, constructors
}
Katakan dalam program anda, anda telah mencipta dua objek
Orang di tempat yang berbeza dan ingin membandingkannya.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Dari perspektif perniagaan, kedua-duanya kelihatan sama, bukan? Tetapi untuk JVM mereka tidak sama. Memandangkan kedua-duanya dicipta menggunakan kata kunci
baharu , tika ini terletak dalam segmen memori yang berbeza. Oleh itu operator
== akan mengembalikan
false . Tetapi jika kita tidak boleh mengatasi
== operator , maka bagaimana kita memberitahu JVM bahawa kita mahu kedua-dua objek ini diperlakukan sama?
Di sinilah kaedah .equals() memainkan peranan . Anda boleh mengatasi
equals() untuk menyemak sama ada sesetengah objek mempunyai nilai yang sama untuk medan tertentu untuk menganggapnya sama. Anda boleh memilih medan yang hendak dibandingkan. Jika kita mengatakan bahawa dua objek
Orang akan sama hanya jika mereka mempunyai umur yang sama dan nama yang sama, maka IDE akan menjana sesuatu seperti ini untuk mencipta
equals() secara automatik :
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
Mari kita kembali kepada contoh sebelumnya.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
Ya, kita tidak boleh membebankan operator
== untuk membandingkan objek dengan cara yang kita mahu, tetapi Java memberi kita cara lain - kaedah
equals() , yang boleh kita ganti mengikut kehendak kita.
Perlu diingat bahawa jika kami tidak menyediakan versi tersuai kami .equals() (juga dikenali sebagai override) dalam kelas kami, maka .equals() yang dipratentukan daripada kelas Object dan operator == akan berkelakuan sama. Kaedah lalai
equals() , diwarisi daripada
Object , akan menyemak sama ada kedua-dua kejadian yang dibandingkan adalah sama dalam ingatan!
Mengapa kita mengatasi kaedah hashCode()?
Sesetengah struktur data dalam Java, seperti
HashSet dan
HashMap , menyimpan elemennya berdasarkan fungsi cincang yang digunakan pada elemen tersebut. Fungsi hash ialah
hashCode() . Jika kita mempunyai pilihan untuk mengatasi kaedah
.equals() , maka kita juga harus mempunyai pilihan untuk mengatasi kaedah
hashCode() . Ada sebab untuk ini. Lagipun, pelaksanaan lalai
hashCode() , yang diwarisi daripada
Object , menganggap semua objek dalam ingatan sebagai unik! Tetapi mari kita kembali kepada struktur data cincang ini. Terdapat peraturan untuk struktur data ini.
HashSet tidak boleh mengandungi nilai pendua dan HashMap tidak boleh mengandungi kunci pendua. HashSet dilaksanakan menggunakan
HashMap dengan cara yang setiap nilai
HashSet disimpan sebagai kunci dalam
HashMap . Bagaimanakah
HashMap berfungsi ?
HashMap ialah tatasusunan asli dengan berbilang segmen. Setiap segmen mempunyai senarai terpaut (
linkedList ). Senarai terpaut ini menyimpan kunci kami.
HashMap mencari linkedList yang betul untuk setiap kunci menggunakan kaedah
hashCode() , dan kemudian melelang melalui semua elemen
linkedList itu dan menggunakan kaedah
equals() pada setiap elemen tersebut untuk menyemak sama ada elemen itu terkandung di sana.
Kunci pendua tidak dibenarkan. Apabila kami meletakkan sesuatu di dalam
HashMap , kunci itu disimpan dalam salah satu senarai terpaut ini. Senarai terpaut mana kunci ini akan disimpan dalam ditunjukkan oleh hasil kaedah
hashCode() untuk kunci itu. Iaitu, jika
key1.hashCode() menghasilkan 4, maka
key1 itu akan disimpan dalam segmen ke-4 tatasusunan dalam
LinkedList yang sedia ada di sana . Secara lalai, kaedah
hashCode() mengembalikan hasil yang berbeza untuk setiap contoh. Jika kita mempunyai
equals() lalai yang berkelakuan seperti
== , menganggap semua kejadian dalam memori sebagai objek berbeza, maka tidak akan ada masalah. Seperti yang anda ingat, dalam contoh kami sebelum ini, kami mengatakan bahawa kami mahu contoh
Orang dianggap sama jika umur dan nama mereka adalah sama.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1.equals(person2) ); --> will print true!
Sekarang mari kita buat peta untuk menyimpan kejadian ini sebagai kunci dengan rentetan tertentu sebagai pasangan nilai.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Dalam kelas
Person , kami tidak mengatasi kaedah
hashCode , tetapi kami mempunyai kaedah
equals yang ditindih .
Memandangkan hashCode lalai memberikan hasil yang berbeza untuk contoh Java yang berbeza
bagi person1.hashCode() dan
person2.hashCode() , terdapat peluang yang tinggi untuk mendapatkan hasil yang berbeza. Peta kami boleh berakhir dengan
orang yang berbeza dalam senarai terpaut yang berbeza.
Ini bertentangan dengan logik
HashMap .
Lagipun, HashMap tidak boleh mempunyai beberapa kunci yang sama! Intinya ialah
hashCode() lalai yang diwarisi daripada kelas
Objek tidak mencukupi. Walaupun selepas kita mengatasi kaedah
equals() kelas
Person . Itulah sebabnya kita perlu mengatasi kaedah
hashCode() selepas kita mengatasi
equals method . Sekarang mari kita betulkan ini. Kami perlu mengatasi kaedah
hashCode() kami supaya ia mengambil kira medan yang sama seperti
equals() , iaitu
age dan
name .
public class Person {
private Integer age;
private String name;
..getters, setters, constructors
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
int prime = 31;
return prime*Objects.hash(name, age);
}
Dalam kaedah
hashCode() kami menggunakan nilai mudah (anda boleh menggunakan sebarang nilai lain). Walau bagaimanapun, adalah dicadangkan untuk menggunakan nombor perdana untuk mencipta lebih sedikit masalah.
Mari cuba simpan kunci ini dalam HashMap kami sekali lagi :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() dan
person2.hashCode() akan menjadi sama. Katakan ia adalah 0.
HashMap akan pergi ke segmen 0 dan di dalamnya
LinkedList akan menyimpan
person1 sebagai kunci dengan nilai "1". Dalam kes kedua, apabila
HashMap pergi ke baldi 0 sekali lagi untuk menyimpan
orang utama2 dengan nilai "2", ia akan melihat bahawa kunci lain yang sama dengannya sudah wujud di sana. Dengan cara ini ia akan menimpa kekunci sebelumnya. Dan hanya
orang2 utama yang akan wujud dalam
HashMap kami . Beginilah kami mempelajari cara peraturan
HashMap berfungsi , yang menyatakan bahawa anda tidak boleh menggunakan berbilang kunci yang sama!
Walau bagaimanapun, perlu diingat bahawa kejadian yang tidak sama rata boleh mempunyai kod cincang yang sama dan tika yang sama mesti mengembalikan kod cincang yang sama.
GO TO FULL VERSION