Mengapa mengganti metode sama dengan dan kode hash di Java?
Sumber:
Medium Artikel ini berfokus pada dua metode yang terkait erat: sama dengan() dan kode hash() . Anda akan mempelajari cara mereka berinteraksi satu sama lain dan cara menggantinya dengan benar.
Mengapa kita mengganti metode sama dengan()?
Di Java, kita tidak bisa membebani perilaku operator seperti
== ,
+= ,
-+ . Mereka bekerja sesuai dengan proses tertentu. Misalnya, perhatikan pengoperasian operator
== .
Bagaimana cara kerja operator ==?
Ia memeriksa apakah dua referensi yang dibandingkan menunjuk ke contoh yang sama di memori. Operator
== hanya akan bernilai true jika kedua referensi mewakili instance yang sama di memori. Mari kita lihat contoh kodenya:
public class Person {
private Integer age;
private String name;
..getters, setters, constructors
}
Katakanlah dalam program Anda, Anda telah membuat dua objek
Person di tempat berbeda dan ingin membandingkannya.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
Dari segi bisnis, keduanya terlihat sama bukan? Namun untuk JVM keduanya tidak sama. Karena keduanya dibuat menggunakan kata kunci
new , instance ini ditempatkan di segmen memori yang berbeda. Oleh karena itu operator
== akan mengembalikan
false . Tetapi jika kita tidak dapat mengganti operator
== , lalu bagaimana kita memberi tahu JVM bahwa kita ingin kedua objek ini diperlakukan sama?
Di sinilah metode .equals() berperan . Anda dapat mengganti
sama dengan() untuk memeriksa apakah beberapa objek memiliki nilai yang sama untuk bidang tertentu agar dianggap sama. Anda dapat memilih bidang mana yang akan dibandingkan. Jika kita mengatakan bahwa dua objek
Person akan sama hanya jika mereka memiliki umur dan nama yang sama, maka IDE akan menghasilkan sesuatu seperti ini untuk secara otomatis membuat
equal() :
@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 ke 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 bisa membebani operator
== secara berlebihan untuk membandingkan objek sesuai keinginan kita, tetapi Java memberi kita cara lain - metode
sama dengan() , yang dapat kita timpa sesuai keinginan.
Ingatlah bahwa jika kita tidak menyediakan versi khusus .equals() (juga dikenal sebagai override) di kelas kita, maka .equals() yang telah ditentukan sebelumnya dari kelas Object dan operator == akan berperilaku sama. Metode equal() default , yang diwarisi dari
Object , akan memeriksa apakah kedua instance yang dibandingkan sama di memori!
Mengapa kita mengganti metode hashCode()?
Beberapa struktur data di Java, seperti
HashSet dan
HashMap , menyimpan elemennya berdasarkan fungsi hash yang diterapkan pada elemen tersebut. Fungsi hashnya adalah
hashCode() . Jika kita mempunyai pilihan untuk mengganti metode
.equals() , maka kita juga harus mempunyai pilihan untuk mengganti metode
hashCode() . Ada alasan untuk ini. Lagi pula, implementasi default
hashCode() , yang diwarisi dari
Object , menganggap semua objek di memori sebagai unik! Tapi mari kita kembali ke struktur data hash ini. Ada aturan untuk struktur data ini.
HashSet tidak boleh berisi nilai duplikat dan HashMap tidak boleh berisi kunci duplikat. HashSet diimplementasikan menggunakan
HashMap sedemikian rupa sehingga setiap nilai
HashSet disimpan sebagai kunci di
HashMap . Bagaimana cara kerja
HashMap ?
HashMap adalah array asli dengan banyak segmen. Setiap segmen memiliki daftar tertaut (
linkedList ). Daftar tertaut ini menyimpan kunci kami.
HashMap menemukan linkedList yang benar untuk setiap kunci menggunakan metode
hashCode() , lalu melakukan iterasi melalui semua elemen
linkedList tersebut dan menerapkan metode
sama dengan() ke setiap elemen tersebut untuk memeriksa apakah elemen tersebut ada di sana.
Kunci duplikat tidak diperbolehkan. Saat kita memasukkan sesuatu ke dalam
HashMap , kuncinya disimpan di salah satu daftar tertaut ini. Daftar tertaut mana yang akan menyimpan kunci ini ditunjukkan oleh hasil metode
hashCode() untuk kunci tersebut. Artinya, jika
key1.hashCode() menghasilkan 4, maka
key1 tersebut akan disimpan di segmen ke-4 array di
LinkedList yang ada di sana . Secara default, metode
hashCode() mengembalikan hasil yang berbeda untuk setiap instance. Jika kita memiliki
equal() default yang berperilaku seperti
== , memperlakukan semua instance di memori sebagai objek berbeda, maka tidak akan ada masalah. Seperti yang mungkin Anda ingat, dalam contoh sebelumnya kami mengatakan bahwa kami ingin instance
Person dianggap sama jika usia dan namanya 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 instance ini sebagai kunci dengan string tertentu sebagai pasangan nilai.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Di kelas
Person , kami belum mengganti metode
kode hash , tetapi kami memiliki metode
sama dengan yang diganti .
Karena hashCode default memberikan hasil yang berbeda untuk instance Java
person1.hashCode() dan
person2.hashCode() yang berbeda , ada kemungkinan besar untuk mendapatkan hasil yang berbeda. Peta kita dapat diakhiri dengan
orang berbeda dalam daftar tertaut berbeda.
Ini bertentangan dengan logika
HashMap .
Lagi pula, HashMap tidak boleh memiliki beberapa kunci yang identik! Intinya adalah
hashCode() default yang diwarisi dari kelas
Object tidaklah cukup. Bahkan setelah kita mengganti metode
sama dengan() dari kelas
Person . Itu sebabnya kita harus mengganti metode
hashCode() setelah kita mengganti metode
yang sama . Sekarang mari kita perbaiki ini. Kita perlu mengganti metode
hashCode() agar memperhitungkan bidang yang sama dengan
same() , yaitu
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 metode
hashCode() kami menggunakan nilai sederhana (Anda dapat menggunakan nilai lainnya). Namun, disarankan untuk menggunakan bilangan prima untuk mengurangi masalah. Mari kita coba simpan lagi kunci-kunci ini di
HashMap kita :
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode() dan
person2.hashCode() akan sama. Katakanlah mereka 0.
HashMap akan menuju ke segmen 0 dan di dalamnya
LinkedList akan menyimpan
person1 sebagai kunci dengan nilai “1”. Dalam kasus kedua, ketika
HashMap pergi ke keranjang 0 lagi untuk menyimpan kunci
person2 dengan nilai “2”, ia akan melihat bahwa kunci lain yang sama dengan itu sudah ada di sana. Dengan cara ini akan menimpa kunci sebelumnya. Dan hanya
person2 kunci yang akan ada di
HashMap kami . Inilah cara kami mempelajari cara kerja aturan
HashMap , yang menyatakan bahwa Anda tidak dapat menggunakan beberapa kunci yang identik!
Namun, perlu diingat bahwa instance yang tidak sama dapat memiliki kode hash yang sama, dan instance yang sama harus mengembalikan kode hash yang sama.
GO TO FULL VERSION