Mengimplementasikan hashCode
Tulis fungsi hash yang benar.
Mengimplementasikan hashCode adalah pelajaran Java Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Java Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Java Academy mencakup 4 pelajaran total.
Tujuan hashCode yang Baik
hashCode() yang baik seharusnya:
- Mengembalikan nilai yang sama untuk objek yang sama (sesuai kontrak).
- Menyebarkan objek yang berbeda ke banyak nilai yang berbeda.
- Murah untuk dihitung.
hashCode buruk yang selalu mengembalikan konstanta tetap memenuhi kontrak, tetapi mengubah peta menjadi daftar berantai yang lambat.
public class Main {
public static void main(String[] args) {
// Legal but terrible: every object collides
System.out.println("constant hashCode is legal but kills performance");
}
}Objects.hash untuk Kasus Umum
Pendekatan benar yang paling sederhana adalah Objects.hash(field1, field2, ...).
Pendekatan ini menangani nilai kosong dan menggabungkan bidang dengan algoritma standar. Gunakan bidang yang sama seperti yang Anda bandingkan dalam equals.
import java.util.Objects;
public class Main {
static class User {
final String name; final int age;
User(String name, int age) { this.name = name; this.age = age; }
@Override public int hashCode() { return Objects.hash(name, age); }
}
public static void main(String[] args) {
User a = new User("Ada", 36);
User b = new User("Ada", 36);
System.out.println(a.hashCode() == b.hashCode());
}
}Pengali Klasik 31
Untuk hash yang ditulis manual, pola standar mengalikan hasil sementara dengan 31, lalu menambahkan hash setiap bidang.
31 adalah bilangan prima ganjil, dan 31 * x sama dengan (x << 5) - x, sehingga JVM dapat mengoptimalkannya.
public class Main {
static class User {
final String name; final int age;
User(String name, int age) { this.name = name; this.age = age; }
@Override public int hashCode() {
int result = 17;
result = 31 * result + (name == null ? 0 : name.hashCode());
result = 31 * result + age;
return result;
}
}
public static void main(String[] args) {
System.out.println(new User("Ada", 36).hashCode());
}
}Menghitung Hash Tipe Primitif
Setiap tipe primitif memiliki cara yang disarankan untuk menghitung hash:
int: gunakan nilainya sendiri.long:(int)(value ^ (value >>> 32)).boolean: 1 atau 0.double:Double.hashCode(value).
public class Main {
public static void main(String[] args) {
long id = 4_000_000_000L;
int longHash = (int) (id ^ (id >>> 32));
System.out.println("long hash: " + longHash);
System.out.println("double hash: " + Double.hashCode(3.14));
System.out.println("bool hash: " + Boolean.hashCode(true));
}
}Menghitung Hash Array
Jangan langsung memanggil hashCode() pada array; cara tersebut menggunakan identitas, bukan isi array.
Gunakan Arrays.hashCode(arr) untuk array datar, atau Arrays.deepHashCode(arr) untuk array bertingkat.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println("identity equal: " + (a.hashCode() == b.hashCode()));
System.out.println("content equal: " + (Arrays.hashCode(a) == Arrays.hashCode(b)));
}
}Selaraskan equals dan hashCode
Bidang yang digunakan dalam hashCode() harus merupakan subset dari bidang yang digunakan dalam equals() (idealnya persis sama).
Jika equals membandingkan lebih banyak bidang daripada hashCode, dua objek yang sama tetap memiliki hash yang sama. Hal itu diperbolehkan. Namun, jika hashCode menggunakan bidang yang diabaikan oleh equals, Anda melanggar kontrak.
import java.util.Objects;
public class Main {
static class Coord {
final int x, y;
Coord(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) {
return o instanceof Coord c && c.x == x && c.y == y;
}
@Override public int hashCode() { return Objects.hash(x, y); }
}
public static void main(String[] args) {
Coord a = new Coord(3, 4), b = new Coord(3, 4);
System.out.println(a.equals(b) && a.hashCode() == b.hashCode());
}
}Menyimpan Hash dalam Cache
Untuk objek yang tidak dapat diubah dengan penghitungan hash yang mahal, Anda dapat menyimpan hasilnya dalam sebuah bidang.
String melakukan hal ini secara internal. Lakukan hanya jika objek benar-benar tidak dapat diubah, sehingga nilai yang disimpan tidak pernah menjadi usang.
import java.util.Objects;
public class Main {
static final class Key {
final String a, b;
private int hash; // 0 until computed
Key(String a, String b) { this.a = a; this.b = b; }
@Override public int hashCode() {
int h = hash;
if (h == 0) { h = Objects.hash(a, b); hash = h; }
return h;
}
}
public static void main(String[] args) {
Key k = new Key("x", "y");
System.out.println(k.hashCode());
System.out.println(k.hashCode());
}
}Distribusi Itu Penting
hashCode yang terdistribusi dengan baik menyebarkan kunci secara merata ke seluruh keranjang. Mari hitung jumlah kode hash yang berbeda untuk sekumpulan objek.
Semakin banyak nilai yang berbeda, semakin sedikit tabrakan dan semakin cepat petanya.
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Main {
record Pair(int a, int b) {}
public static void main(String[] args) {
Set<Integer> hashes = new HashSet<>();
for (int i = 0; i < 100; i++) {
hashes.add(Objects.hash(i, i * 7));
}
System.out.println("distinct hashes: " + hashes.size());
}
}Contoh Distribusi yang Buruk
Menjumlahkan bidang tanpa perkalian menghasilkan tabrakan: (1,2) dan (2,1) sama-sama menghasilkan hash 3.
Pengali 31 memutus simetri ini karena urutan kemudian menjadi penting.
public class Main {
static int badHash(int a, int b) { return a + b; }
static int goodHash(int a, int b) { return 31 * a + b; }
public static void main(String[] args) {
System.out.println("bad (1,2): " + badHash(1, 2) + ", (2,1): " + badHash(2, 1));
System.out.println("good (1,2): " + goodHash(1, 2) + ", (2,1): " + goodHash(2, 1));
}
}Pilih Record untuk Tipe Nilai
Untuk pembawa data murni, record secara otomatis menghasilkan hashCode yang benar dan terdistribusi dengan baik.
Tulis hashCode secara manual hanya jika Anda memerlukan semantik khusus atau tidak dapat menggunakan record.
public class Main {
record Money(long cents, String currency) {}
public static void main(String[] args) {
Money a = new Money(1099, "USD");
Money b = new Money(1099, "USD");
System.out.println(a.equals(b));
System.out.println(a.hashCode() == b.hashCode());
}
}Menggabungkannya
Kelas nilai yang lengkap: bidang yang tidak dapat diubah, equals dan hashCode dari bidang yang sama, serta toString yang rapi.
import java.util.Objects;
public class Main {
static final class Version {
final int major, minor, patch;
Version(int major, int minor, int patch) {
this.major = major; this.minor = minor; this.patch = patch;
}
@Override public boolean equals(Object o) {
return o instanceof Version v && v.major == major && v.minor == minor && v.patch == patch;
}
@Override public int hashCode() { return Objects.hash(major, minor, patch); }
@Override public String toString() { return major + "." + minor + "." + patch; }
}
public static void main(String[] args) {
Version v = new Version(2, 1, 0);
System.out.println(v + " hash=" + v.hashCode());
}
}Pemeriksaan Singkat
Uji keterampilan hashCode Anda.
Rangkuman
Anda telah mempelajari cara mengimplementasikan hashCode dengan benar:
- Gunakan
Objects.hash(...)untuk kasus umum. - Gunakan pola pengali 31 untuk penghitungan hash secara manual.
- Hitung hash array dengan
Arrays.hashCode, bukan dengan cara bawaan. - Selaraskan bidang hashCode dengan equals, dan pilih record jika memungkinkan.
Berikutnya, lihat cara Java 8+ mengubah keranjang yang terlalu penuh menjadi pohon.
import java.util.Objects;
public class Main {
public static void main(String[] args) {
System.out.println("hashCode recap done: " + Objects.hash("done"));
}
}Pertanyaan yang Sering Diajukan
Apakah pelajaran “Mengimplementasikan hashCode” gratis?
Ya — teks lengkap “Mengimplementasikan hashCode” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Java Academy, upgrade ke CoddyKit PRO. Kursus Java Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Mengimplementasikan hashCode”?
Tulis fungsi hash yang benar. Kamu berlatih Java Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Java Academy?
Tidak diperlukan pengalaman sebelumnya. Java Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.
Berapa lama pelajaran “Mengimplementasikan hashCode” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Java Academy ini?
Ya. Setiap pelajaran Java Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Cara Kerja HashMap
- Kontrak equals/hashCode
- Mengimplementasikan hashCode
- Treeification dan Performa