Kontrak equals/hashCode
Sebab kedua-duanya mesti sepadan
Kontrak equals/hashCode ialah pelajaran Java Academy percuma di CoddyKit. Ini ialah pelajaran 2 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Java Academy, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Java Academy merangkumi sejumlah 4 pelajaran.
Dua Kaedah, Satu Kontrak
Setiap objek Java mewarisi equals() dan hashCode() daripada Object.
Apabila Anda menindih salah satunya, Anda hampir selalu mesti menindih yang satu lagi. Kedua-duanya membentuk kontrak yang mengikat dan menjadi asas kepada koleksi berasaskan 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());
}
}Peraturan Teras
Kontrak tersebut menyatakan:
- Jika
a.equals(b)ialah benar, makaa.hashCode()mesti sama denganb.hashCode(). - Jika kod cincangan berbeza, objek tersebut dijamin tidak sama.
Objek yang sama mesti mempunyai kod cincangan yang sama. Perkara sebaliknya 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 yang Tidak Sama Boleh Berkongsi Hash
Dua objek yang tidak sama dibenarkan berkongsi kod cincangan. Itu hanyalah perlanggaran, dan peta mengendalikannya dengan equals().
Jadi, satu-satunya keadaan yang dilarang ialah objek yang sama mempunyai kod cincangan yang berbeza.
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 Menindih equals
Berikut ialah kelas yang rosak. Kelas ini menindih equals() tetapi bukan hashCode().
Dua titik yang sama secara logik kini mempunyai kod cincangan yang berbeza, yang diwarisi daripada kelas asal dan 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()));
}
}Pepijat dalam HashSet
Kelas yang rosak gagal dalam HashSet. Set mencari dalam baket yang dipilih oleh hash yang salah lalu tidak pernah menemui elemen yang sama.
Akibatnya, Anda mendapat pendua yang sepatutnya 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());
}
}Membetulkannya
Tindih kedua-dua kaedah dan hasilkannya daripada medan yang sama. Dengan itu, objek yang sama berkongsi hash dan masuk ke baket 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 Mesti Refleksif
equals() yang betul mematuhi empat sifat. Sifat pertama ialah refleksif: x.equals(x) mesti bernilai benar.
Hal ini kedengaran jelas, tetapi logik tersuai boleh melanggarnya jika Anda terlupa mengendalikan kes rujukan yang sama.
public class Main {
public static void main(String[] args) {
String x = "java";
System.out.println(x.equals(x));
}
}Simetri dan Transitif
Dua lagi peraturan:
- Simetri: jika
a.equals(b), makab.equals(a). - Transitif: jika
a.equals(b)danb.equals(c), makaa.equals(c).
Mencampurkan jenis merentas hierarki kelas sering memecahkan kesimetrian, jadi bandingkan kelas dengan teliti.
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
Peraturan keempat ialah konsisten: panggilan berulang memulangkan hasil yang sama selagi objek tidak berubah.
Inilah sebabnya Anda patut berdasarkan equals dan hashCode pada medan yang tidak berubah. Mengubah medan yang digunakan dalam hashCode selepas pemasukan akan merosakkan 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());
}
}Rekod Melakukannya untuk Anda
record Java menjana equals() dan hashCode() secara automatik daripada semua komponennya.
Untuk data yang menyerupai nilai, utamakan rekod. Kontrak dipenuhi secara percuma dan sentiasa 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());
}
}Perangkap Kunci Boleh Diubah
Jika Anda mengubah medan yang digunakan dalam hashCode selepas memasukkan objek ke dalam peta, Anda tidak lagi dapat mencarinya.
Peta mencari dalam baket lama, sedangkan objek itu kini dicincang kepada baket baharu. Entri tersebut 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));
}
}Semakan Pantas
Uji pemahaman Anda tentang kontrak tersebut.
Rumusan
Anda telah mempelajari kontrak equals/hashCode:
- Objek yang sama mesti mempunyai kod cincangan yang sama.
- equals mesti refleksif, simetri, transitif dan konsisten.
- Asaskan kedua-dua kaedah pada medan tidak berubah yang sama.
- Rekod menjana pelaksanaan yang betul secara percuma.
Seterusnya, Anda akan menulis hashCode yang betul dengan tangan.
import java.util.Objects;
public class Main {
public static void main(String[] args) {
System.out.println("Contract recap: " + Objects.hash(1, 2, 3));
}
}Pelajari Java dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 104
- Pelajaran
- 374
Soalan Lazim
Adakah pelajaran “Kontrak equals/hashCode” percuma?
Ya — teks penuh “Kontrak equals/hashCode” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Java Academy, tingkat taraf kepada CoddyKit PRO. Kursus Java Academy merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Kontrak equals/hashCode”?
Sebab kedua-duanya mesti sepadan Anda berlatih Java Academy menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Java Academy?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Java Academy di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 2 daripada 4.
Berapa lamakah pelajaran “Kontrak equals/hashCode” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Java Academy ini?
Ya. Setiap pelajaran Java Academy menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Cara HashMap Berfungsi
- Kontrak equals/hashCode
- Melaksanakan hashCode
- Pohon dan Prestasi