0Pricing
Java Academy · Pelajaran

Kontrak equals/hashCode

Mengapa keduanya harus selaras.

Kontrak equals/hashCode adalah pelajaran Java Academy gratis di CoddyKit. Ini adalah pelajaran 2 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.

Dua Metode, Satu Kontrak

Setiap objek Java mewarisi equals() dan hashCode() dari Object.

Saat Anda menimpa salah satunya, hampir selalu Anda harus menimpa yang lainnya juga. Keduanya membentuk kontrak yang mengikat dan menjadi dasar bagi koleksi berbasis hash.

public class Main {
    public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();
        System.out.println(a.equals(b));
        System.out.println(a.hashCode() == b.hashCode());
    }
}

Aturan Inti

Kontrak tersebut menyatakan:

  • Jika a.equals(b) bernilai true, maka a.hashCode() harus sama dengan b.hashCode().
  • Jika kode hash berbeda, objek-objek tersebut dipastikan tidak sama.

Objek yang sama harus memiliki kode hash yang sama. Kebalikannya tidak diwajibkan.

public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hel" + "lo";
        System.out.println(s1.equals(s2));
        System.out.println(s1.hashCode() == s2.hashCode());
    }
}

Objek Berbeda Boleh Memiliki Hash yang Sama

Dua objek yang berbeda diperbolehkan memiliki kode hash yang sama. Itu hanyalah sebuah tabrakan, dan peta menanganinya dengan equals().

Jadi, satu-satunya situasi yang dilarang adalah objek yang sama memiliki kode hash yang berbeda.

public class Main {
    public static void main(String[] args) {
        // Equal value, unequal objects, same hash is fine
        System.out.println("FB".hashCode() == "Ea".hashCode());
        System.out.println("FB".equals("Ea"));
    }
}

Kelas yang Hanya Menimpa equals

Berikut adalah kelas yang rusak. Kelas ini menimpa equals(), tetapi tidak menimpa hashCode().

Dua titik yang secara logis sama kini memiliki kode hash berbeda yang diwarisi dari Object, sehingga melanggar kontrak.

public class Main {
    static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) {
            if (!(o instanceof Point p)) return false;
            return x == p.x && y == p.y;
        }
        // BUG: no hashCode override
    }
    public static void main(String[] args) {
        Point a = new Point(1, 2);
        Point b = new Point(1, 2);
        System.out.println("equals: " + a.equals(b));
        System.out.println("same hash: " + (a.hashCode() == b.hashCode()));
    }
}

Kesalahan dalam HashSet

Kelas yang rusak tersebut gagal saat digunakan dalam HashSet. Himpunan mencari di keranjang yang dipilih oleh hash yang salah dan tidak pernah menemukan elemen yang sama.

Akibatnya, Anda mendapatkan duplikat yang seharusnya ditolak.

import java.util.HashSet;
import java.util.Set;

public class Main {
    static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) {
            if (!(o instanceof Point p)) return false;
            return x == p.x && y == p.y;
        }
    }
    public static void main(String[] args) {
        Set<Point> set = new HashSet<>();
        set.add(new Point(1, 2));
        set.add(new Point(1, 2));
        System.out.println("size = " + set.size());
    }
}

Memperbaikinya

Timpa kedua metode dan turunkan hasilnya dari bidang yang sama. Dengan demikian, objek yang sama akan memiliki hash yang sama dan masuk ke keranjang yang sama.

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Main {
    static class Point {
        final int x, y;
        Point(int x, int y) { this.x = x; this.y = y; }
        @Override public boolean equals(Object o) {
            if (!(o instanceof Point p)) return false;
            return x == p.x && y == p.y;
        }
        @Override public int hashCode() { return Objects.hash(x, y); }
    }
    public static void main(String[] args) {
        Set<Point> set = new HashSet<>();
        set.add(new Point(1, 2));
        set.add(new Point(1, 2));
        System.out.println("size = " + set.size());
    }
}

equals Harus Bersifat Refleksif

equals() yang benar mematuhi empat sifat. Sifat pertama adalah refleksif: x.equals(x) harus bernilai true.

Hal ini terdengar jelas, tetapi logika khusus dapat merusaknya jika Anda lupa menangani kasus referensi yang sama.

public class Main {
    public static void main(String[] args) {
        String x = "java";
        System.out.println(x.equals(x));
    }
}

Simetris dan Transitif

Dua aturan lainnya:

  • Simetris: jika a.equals(b), maka b.equals(a).
  • Transitif: jika a.equals(b) dan b.equals(c), maka a.equals(c).

Mencampurkan tipe di seluruh hierarki kelas sering kali merusak sifat simetris, jadi bandingkan kelas dengan cermat.

public class Main {
    public static void main(String[] args) {
        String a = "x", b = "x", c = "x";
        boolean symmetric = a.equals(b) == b.equals(a);
        boolean transitive = a.equals(b) && b.equals(c) && a.equals(c);
        System.out.println("symmetric: " + symmetric);
        System.out.println("transitive: " + transitive);
    }
}

Konsisten

Aturan keempat adalah konsisten: pemanggilan berulang mengembalikan hasil yang sama selama objek tidak berubah.

Inilah alasan Anda sebaiknya mendasarkan equals dan hashCode pada bidang yang tidak dapat diubah. Mengubah bidang yang digunakan dalam hashCode setelah penyisipan akan merusak peta.

import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        // Immutable record gives consistent equals/hashCode automatically
        record Id(int value) {}
        Id id = new Id(7);
        System.out.println(Objects.equals(id, new Id(7)));
        System.out.println(id.hashCode() == new Id(7).hashCode());
    }
}

Record Melakukannya untuk Anda

record Java secara otomatis menghasilkan equals() dan hashCode() dari semua komponennya.

Untuk data yang menyerupai nilai, pilihlah record. Kontrak terpenuhi secara otomatis dan tetap selaras.

import java.util.HashSet;
import java.util.Set;

public class Main {
    record Point(int x, int y) {}
    public static void main(String[] args) {
        Set<Point> set = new HashSet<>();
        set.add(new Point(1, 2));
        set.add(new Point(1, 2));
        System.out.println("size = " + set.size());
    }
}

Jebakan Kunci yang Dapat Diubah

Jika Anda mengubah bidang yang digunakan dalam hashCode setelah memasukkan objek ke dalam peta, Anda tidak akan dapat menemukannya lagi.

Peta mencari di keranjang lama, sedangkan objek tersebut kini menghasilkan hash untuk keranjang baru. Entri itu menjadi hantu yang hilang.

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Main {
    static class Box {
        int id;
        Box(int id) { this.id = id; }
        @Override public boolean equals(Object o) {
            return o instanceof Box b && b.id == id;
        }
        @Override public int hashCode() { return Objects.hash(id); }
    }
    public static void main(String[] args) {
        Map<Box, String> m = new HashMap<>();
        Box key = new Box(1);
        m.put(key, "value");
        key.id = 99; // mutated after insertion
        System.out.println(m.get(key));
    }
}

Pemeriksaan Singkat

Uji pemahaman Anda tentang kontrak tersebut.

Rangkuman

Anda telah mempelajari kontrak equals/hashCode:

  • Objek yang sama harus memiliki kode hash yang sama.
  • equals harus bersifat refleksif, simetris, transitif, dan konsisten.
  • Dasarkan kedua metode pada bidang yang tidak dapat diubah dan sama.
  • Record menghasilkan implementasi yang benar secara otomatis.

Berikutnya, Anda akan menulis hashCode yang benar secara manual.

import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        System.out.println("Contract recap: " + Objects.hash(1, 2, 3));
    }
}

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Kontrak equals/hashCode” gratis?

Ya — teks lengkap “Kontrak equals/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 “Kontrak equals/hashCode”?

Mengapa keduanya harus selaras. 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 2 dari 4.

Berapa lama pelajaran “Kontrak equals/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

  1. Cara Kerja HashMap
  2. Kontrak equals/hashCode
  3. Mengimplementasikan hashCode
  4. Treeification dan Performa
← Kembali ke Java Academy