Masalah / perangkap apa yang harus dipertimbangkan ketika mengesampingkan equals
dan hashCode
?
sumber
Masalah / perangkap apa yang harus dipertimbangkan ketika mengesampingkan equals
dan hashCode
?
equals()
( javadoc ) harus mendefinisikan hubungan ekivalensi (harus refleksif , simetris , dan transitif ). Selain itu, harus konsisten (jika objek tidak dimodifikasi, maka ia harus tetap mengembalikan nilai yang sama). Selanjutnya, o.equals(null)
harus selalu mengembalikan false.
hashCode()
( javadoc ) juga harus konsisten (jika objek tidak dimodifikasi dalam hal equals()
, harus tetap mengembalikan nilai yang sama).
The hubungan antara dua metode adalah:
Kapanpun
a.equals(b)
, makaa.hashCode()
harus sama denganb.hashCode()
.
Jika Anda menimpa satu, maka Anda harus menimpa yang lain.
Gunakan kumpulan bidang yang sama yang Anda gunakan untuk menghitung equals()
untuk menghitung hashCode()
.
Gunakan kelas pembantu yang sangat baik, EqualsBuilder dan HashCodeBuilder dari perpustakaan Apache Commons Lang . Sebuah contoh:
public class Person {
private String name;
private int age;
// ...
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers
// if deriving: appendSuper(super.hashCode()).
append(name).
append(age).
toHashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
return false;
if (obj == this)
return true;
Person rhs = (Person) obj;
return new EqualsBuilder().
// if deriving: appendSuper(super.equals(obj)).
append(name, rhs.name).
append(age, rhs.age).
isEquals();
}
}
Saat menggunakan Koleksi atau Peta berbasis hash seperti HashSet , LinkedHashSet , HashMap , Hashtable , atau WeakHashMap , pastikan kode hash () dari objek kunci yang Anda masukkan ke dalam koleksi tidak pernah berubah saat objek berada di koleksi. Cara anti peluru untuk memastikan ini adalah membuat kunci Anda tidak berubah, yang juga memiliki manfaat lain .
instanceof
mengembalikan false jika operan pertamanya nol (Java lagi efektif)Ada beberapa masalah yang perlu diperhatikan jika Anda berurusan dengan kelas yang bertahan menggunakan Object-Relationship Mapper (ORM) seperti Hibernate, jika Anda tidak berpikir ini sudah terlalu rumit rumit!
Objek yang dimuat malas adalah subclass
Jika objek Anda tetap menggunakan ORM, dalam banyak kasus Anda akan berhadapan dengan proxy dinamis untuk menghindari memuat objek terlalu dini dari penyimpanan data. Proxy ini diimplementasikan sebagai subclass dari kelas Anda sendiri. Ini berarti
this.getClass() == o.getClass()
akan kembalifalse
. Sebagai contoh:Jika Anda berurusan dengan ORM, gunakan
o instanceof Person
adalah satu-satunya hal yang akan berperilaku benar.Objek malas yang dimuat memiliki bidang-nol
ORM biasanya menggunakan getter untuk memaksa pemuatan objek bermuatan malas. Ini berarti bahwa
person.name
akannull
jikaperson
dimuat malas, bahkan jikaperson.getName()
memaksa memuat dan mengembalikan "John Doe". Dalam pengalaman saya, ini muncul lebih sering dihashCode()
danequals()
.Jika Anda berurusan dengan ORM, pastikan untuk selalu menggunakan getter, dan jangan pernah referensi bidang di
hashCode()
danequals()
.Menyimpan objek akan mengubah kondisinya
Objek persisten sering menggunakan
id
bidang untuk menahan kunci objek. Bidang ini akan diperbarui secara otomatis saat sebuah objek disimpan pertama kali. Jangan gunakan bidang id dihashCode()
. Tapi Anda bisa menggunakannyaequals()
.Pola yang sering saya gunakan adalah
Tetapi: Anda tidak dapat menyertakan
getId()
dihashCode()
. Jika Anda melakukannya, ketika suatu objek bertahan, ituhashCode
perubahannya. Jika objek berada dalam aHashSet
, Anda "tidak akan" menemukannya lagi.Dalam
Person
contoh saya , saya mungkin akan menggunakangetName()
untukhashCode
dangetId()
plusgetName()
(hanya untuk paranoia) untukequals()
. Tidak apa-apa jika ada beberapa risiko "tabrakan" untukhashCode()
, tetapi tidak pernah baik untuk ituequals()
.hashCode()
harus menggunakan subset properti yang tidak berubah dariequals()
sumber
Saving an object will change it's state
!hashCode
harus kembaliint
, jadi bagaimana Anda akan menggunakangetName()
? Bisakah Anda memberi contoh untuk AndahashCode
Klarifikasi tentang
obj.getClass() != getClass()
.Pernyataan ini adalah hasil dari
equals()
pewarisan yang tidak ramah. JLS (spesifikasi bahasa Java) menetapkan bahwa jikaA.equals(B) == true
kemudianB.equals(A)
harus juga kembalitrue
. Jika Anda mengabaikan pernyataan yang mewarisi kelas yang menimpaequals()
(dan mengubah perilakunya) akan melanggar spesifikasi ini.Pertimbangkan contoh berikut tentang apa yang terjadi ketika pernyataan dihilangkan:
Melakukan
new A(1).equals(new A(1))
Juga,new B(1,1).equals(new B(1,1))
hasil memberi yang benar, sebagaimana mestinya.Ini terlihat sangat bagus, tetapi lihat apa yang terjadi jika kita mencoba menggunakan kedua kelas:
Jelas, ini salah.
Jika Anda ingin memastikan kondisi simetris. a = b jika b = a dan panggilan prinsip substitusi Liskov
super.equals(other)
tidak hanya dalam halB
instance, tetapi periksa jugaA
misalnya:Yang akan menghasilkan:
Di mana, jika
a
bukan referensiB
, maka itu mungkin menjadi referensi kelasA
(karena Anda memperluasnya), dalam hal ini Anda meneleponsuper.equals()
juga .sumber
ThingWithOptionSetA
dapat sama denganThing
asalkan semua opsi tambahan memiliki nilai default, dan juga untuk aThingWithOptionSetB
, maka harus mungkin untukThingWithOptionSetA
dibandingkan denganThingWithOptionSetB
hanya jika semua properti non-basis dari kedua objek cocok dengan default mereka, tetapi Saya tidak melihat bagaimana Anda menguji itu.B b2 = new B(1,99)
, lalub.equals(a) == true
dana.equals(b2) == true
tetapib.equals(b2) == false
.Untuk implementasi yang ramah-warisan, lihat solusi Tal Cohen, Bagaimana Saya Menerapkan Metode equals () dengan benar?
Ringkasan:
Dalam bukunya Panduan Bahasa Pemrograman Java yang Efektif (Addison-Wesley, 2001), Joshua Bloch mengklaim bahwa "Tidak ada cara untuk memperluas kelas yang mungkin ada dan menambahkan aspek sambil mempertahankan kontrak yang sama." Tal tidak setuju.
Solusinya adalah mengimplementasikan equals () dengan memanggil blindlyEquals nonsimetrik lain () keduanya. blindlyEquals () ditimpa oleh subclass, equals () diwarisi, dan tidak pernah diganti.
Contoh:
Perhatikan bahwa equals () harus bekerja lintas hierarki warisan jika Prinsip Substitusi Liskov harus dipenuhi.
sumber
if (this.getClass() != o.getClass()) return false
, tetapi fleksibel karena hanya mengembalikan false jika kelas turunannya repot untuk memodifikasi sama. Apakah itu benar?Masih heran bahwa tidak ada yang merekomendasikan perpustakaan jambu biji untuk ini.
sumber
this
dithis.getDate()
berarti apa-apa (selain kekacauan)if (!(otherObject instanceof DateAndPattern)) {
. Setuju dengan hernan dan Steve Kuo (meskipun itu masalah pilihan pribadi), tapi tetap saja +1.Ada dua metode dalam kelas super sebagai java.lang.Object. Kita perlu menimpanya ke objek kustom.
Objek yang sama harus menghasilkan kode hash yang sama selama mereka sama, namun objek yang tidak sama tidak perlu menghasilkan kode hash yang berbeda.
Jika Anda ingin mendapatkan lebih banyak, silakan periksa tautan ini sebagai http://www.javaranch.com/journal/2002/10/equalhash.html
Ini adalah contoh lain, http://java67.blogspot.com/2013/04/example-of-overriding-equals-hashcode-compareTo-java-method.html
Selamat bersenang-senang! @. @
sumber
Ada beberapa cara untuk melakukan pemeriksaan kesetaraan kelas sebelum memeriksa kesetaraan anggota, dan saya pikir keduanya berguna dalam keadaan yang tepat.
instanceof
operator.this.getClass().equals(that.getClass())
.Saya menggunakan # 1 dalam
final
implementasi equals, atau ketika mengimplementasikan antarmuka yang menentukan algoritma untuk equals (sepertijava.util
antarmuka koleksi — cara yang tepat untuk memeriksa dengan(obj instanceof Set)
atau antarmuka apa pun yang Anda laksanakan). Ini umumnya merupakan pilihan yang buruk ketika persamaan dapat ditimpa karena itu merusak properti simetri.Opsi # 2 memungkinkan kelas untuk diperpanjang dengan aman tanpa mengesampingkan sama atau melanggar simetri.
Jika kelas Anda juga
Comparable
, metodeequals
dancompareTo
harus konsisten juga. Inilah templat untuk metode sama dengan diComparable
kelas:sumber
final
, dancompareTo()
metodenya diganti untuk membalik urutan, contoh dari subclass dan superclass tidak boleh dianggap sama. Ketika objek-objek ini digunakan bersama-sama di pohon, kunci yang "sama" sesuai denganinstanceof
implementasi mungkin tidak dapat ditemukan.Untuk yang sederajat, lihatlah Rahasia dari Persamaan oleh Angelika Langer . Saya sangat menyukainya. Dia juga FAQ yang bagus tentang Generik di Jawa . Lihat artikel lainnya di sini (gulir ke bawah ke "Core Java"), di mana ia juga melanjutkan dengan Bagian-2 dan "perbandingan tipe campuran". Selamat membaca!
sumber
metode equals () digunakan untuk menentukan kesetaraan dua objek.
karena nilai int 10 selalu sama dengan 10. Tapi metode equals () ini adalah tentang persamaan dua objek. Ketika kita mengatakan objek, ia akan memiliki properti. Untuk memutuskan kesetaraan sifat-sifat tersebut dipertimbangkan. Tidak perlu bahwa semua properti harus diperhitungkan untuk menentukan kesetaraan dan sehubungan dengan definisi dan konteks kelas itu dapat diputuskan. Kemudian metode equals () dapat diganti.
kita harus selalu mengganti metode hashCode () setiap kali kita mengganti metode equals (). Jika tidak, apa yang akan terjadi? Jika kita menggunakan hashtable dalam aplikasi kita, itu tidak akan berlaku seperti yang diharapkan. Karena kode hash digunakan dalam menentukan kesetaraan nilai yang disimpan, kode hash tidak akan mengembalikan nilai terkait yang tepat untuk kunci.
Implementasi default yang diberikan adalah metode hashCode () di kelas Object menggunakan alamat internal objek dan mengubahnya menjadi integer dan mengembalikannya.
Output Kode Contoh:
sumber
Secara logis kami memiliki:
a.getClass().equals(b.getClass()) && a.equals(b)
⇒a.hashCode() == b.hashCode()
Tapi tidak sebaliknya!
sumber
Satu gotcha yang saya temukan adalah di mana dua objek berisi referensi satu sama lain (satu contoh menjadi hubungan orang tua / anak dengan metode kenyamanan pada orang tua untuk mendapatkan semua anak).
Hal-hal semacam ini cukup umum ketika melakukan pemetaan Hibernate misalnya.
Jika Anda memasukkan kedua ujung hubungan dalam kode hash Anda atau sama dengan tes itu mungkin untuk masuk ke loop rekursif yang berakhir dengan StackOverflowException.
Solusi paling sederhana adalah tidak memasukkan koleksi getChildren dalam metode.
sumber
equals()
. Jika seorang ilmuwan gila membuat duplikat saya, kami akan setara. Tetapi kita tidak akan memiliki ayah yang sama.