Implementasi terbaik? Itu adalah pertanyaan yang sulit karena itu tergantung pada pola penggunaan.
A untuk hampir semua kasus, implementasi yang baik dan wajar diusulkan dalam Josh Bloch 's Java yang Efektif pada Butir 8 (edisi kedua). Yang terbaik adalah mencarinya di sana karena penulis menjelaskan di sana mengapa pendekatannya bagus.
Versi singkat
Buat int resultdan tetapkan nilai yang bukan nol .
Untuk setiap bidang yangf diuji dalam equals()metode ini, hitung kode hash cdengan:
Jika bidang f adalah boolean: hitung (f ? 0 : 1);
Jika bidang f adalah byte, char, shortatau int: menghitung (int)f;
Jika bidang f adalah long: hitung (int)(f ^ (f >>> 32));
Jika bidang f adalah float: hitung Float.floatToIntBits(f);
Jika bidang f adalah double: menghitung Double.doubleToLongBits(f)dan menangani nilai pengembalian seperti setiap nilai panjang;
Jika bidang f adalah objek : Gunakan hasil dari hashCode()metode atau 0 jika f == null;
Jika bidang f adalah array : lihat setiap bidang sebagai elemen terpisah dan hitung nilai hash secara rekursif dan gabungkan nilai-nilai seperti yang dijelaskan berikutnya.
Gabungkan nilai hash cdengan result:
result =37* result + c
Kembali result
Ini harus menghasilkan distribusi nilai hash yang tepat untuk sebagian besar situasi penggunaan.
Ya saya sangat ingin tahu tentang dari mana nomor 37 berasal.
Kip
17
Saya menggunakan item 8 dari buku "Java Efektif" Josh Bloch.
tuan
39
@dma_k Alasan menggunakan bilangan prima dan metode yang dijelaskan dalam jawaban ini adalah untuk memastikan bahwa kode hash yang dikomputasi akan menjadi unik . Saat menggunakan nomor non-prima, Anda tidak dapat menjamin ini. Tidak masalah nomor utama mana yang Anda pilih, tidak ada yang ajaib tentang nomor 37 (terlalu buruk 42 bukan angka utama, eh?)
Simon Forsberg
34
@ SimonAndréForsberg Yah, kode hash yang dikomputasi tidak selalu unik :) Adalah kode hash. Namun saya mendapat ide: bilangan prima hanya memiliki satu pengganda, sedangkan non-prima memiliki setidaknya dua. Itu menciptakan kombinasi ekstra untuk operator perkalian untuk menghasilkan hash yang sama, yaitu menyebabkan tabrakan.
Jika Anda senang dengan implementasi Java Efektif yang direkomendasikan oleh dmeister, Anda dapat menggunakan panggilan perpustakaan alih-alih memutar sendiri:
Ini membutuhkan Guava ( com.google.common.base.Objects.hashCode) atau pustaka standar di Java 7 ( java.util.Objects.hash) tetapi bekerja dengan cara yang sama.
Kecuali jika seseorang memiliki alasan yang kuat untuk tidak menggunakan ini, orang pasti harus menggunakan ini dalam hal apa pun. (Merumuskannya lebih kuat, karena IMHO harus dirumuskan.) Argumen khas untuk menggunakan implementasi standar / perpustakaan berlaku (praktik terbaik, teruji dengan baik, lebih sedikit rawan kesalahan, dll).
Kissaki
7
@ justin.hughey kau sepertinya bingung. Satu-satunya kasus yang Anda harus timpa hashCodeadalah jika Anda memiliki kebiasaan equals, dan itulah tepatnya metode perpustakaan ini dirancang untuk. Dokumentasi ini cukup jelas tentang perilaku mereka terkait equals. Implementasi perpustakaan tidak mengklaim untuk membebaskan Anda dari mengetahui apa karakteristik hashCodeimplementasi yang benar - perpustakaan ini memudahkan Anda untuk mengimplementasikan implementasi yang sesuai untuk sebagian besar kasus di mana equalsditimpa.
bacar
6
Untuk setiap pengembang Android yang melihat kelas java.util.Objects, itu hanya diperkenalkan di API 19, jadi pastikan Anda menjalankan di KitKat atau di atas, jika tidak Anda akan mendapatkan NoClassDefFoundError.
Andrew Kelly
3
Jawaban terbaik IMO, meskipun dengan contoh saya lebih suka memilih java.util.Objects.hash(...)metode JDK7 daripada com.google.common.base.Objects.hashCode(...)metode jambu biji . Saya pikir kebanyakan orang akan memilih perpustakaan standar daripada ketergantungan ekstra.
Malte Skoruppa
2
Jika ada dua argumen atau lebih dan jika salah satu dari mereka adalah array, hasilnya mungkin bukan yang Anda harapkan karena hashCode()untuk array hanya itu java.lang.System.identityHashCode(...).
starikoff
59
Lebih baik menggunakan fungsi yang disediakan oleh Eclipse yang melakukan pekerjaan yang cukup bagus dan Anda dapat menempatkan upaya dan energi Anda dalam mengembangkan logika bisnis.
+1 Solusi praktis yang bagus. Solusi dmeister lebih komprehensif, tetapi saya cenderung lupa untuk menangani null ketika saya mencoba menulis kode hash sendiri.
Quantum7
1
+1 Setuju dengan Quantum7, tapi saya akan mengatakan itu juga sangat bagus untuk memahami apa yang dilakukan implementasi Eclipse, dan dari mana ia mendapatkan detail implementasinya.
jwir3
15
Maaf tetapi jawaban yang melibatkan "fungsionalitas yang disediakan oleh [beberapa IDE]" tidak benar-benar relevan dalam konteks bahasa pemrograman secara umum. Ada puluhan IDE dan ini tidak menjawab pertanyaan ... yaitu karena ini lebih tentang penentuan algoritmik dan terkait langsung dengan implementasi equals () - sesuatu yang tidak diketahui oleh IDE.
@Overridepublicint hashCode(){// Start with a non-zero constant. Prime is preferredint result =17;// Include a hash for each field.// Primatives
result =31* result +(booleanField ?1:0);// 1 bit » 32-bit
result =31* result + byteField;// 8 bits » 32-bit
result =31* result + charField;// 16 bits » 32-bit
result =31* result + shortField;// 16 bits » 32-bit
result =31* result + intField;// 32 bits » 32-bit
result =31* result +(int)(longField ^(longField >>>32));// 64 bits » 32-bit
result =31* result +Float.floatToIntBits(floatField);// 32 bits » 32-bitlong doubleFieldBits =Double.doubleToLongBits(doubleField);// 64 bits (double) » 64-bit (long) » 32-bit (int)
result =31* result +(int)(doubleFieldBits ^(doubleFieldBits >>>32));// Objects
result =31* result +Arrays.hashCode(arrayField);// var bits » 32-bit
result =31* result + referenceField.hashCode();// var bits » 32-bit (non-nullable)
result =31* result +// var bits » 32-bit (nullable) (nullableReferenceField ==null?0: nullableReferenceField.hashCode());return result;}
EDIT
Biasanya, saat Anda menimpa hashcode(...), Anda juga ingin menimpa equals(...). Jadi bagi yang mau atau sudah menerapkan equals, berikut ini adalah referensi yang bagus dari Github saya ...
@Overridepublicboolean equals(Object o){// Optimization (not required).if(this== o){returntrue;}// Return false if the other object has the wrong type, interface, or is null.if(!(o instanceofMyType)){returnfalse;}MyType lhs =(MyType) o;// lhs means "left hand side"// Primitive fieldsreturn booleanField == lhs.booleanField
&& byteField == lhs.byteField
&& charField == lhs.charField
&& shortField == lhs.shortField
&& intField == lhs.intField
&& longField == lhs.longField
&& floatField == lhs.floatField
&& doubleField == lhs.doubleField
// Arrays&&Arrays.equals(arrayField, lhs.arrayField)// Objects&& referenceField.equals(lhs.referenceField)&&(nullableReferenceField ==null? lhs.nullableReferenceField ==null: nullableReferenceField.equals(lhs.nullableReferenceField));}
jika equals () mengembalikan true untuk dua objek, maka hashCode () harus mengembalikan nilai yang sama. Jika equals () mengembalikan false, maka hashCode () harus mengembalikan nilai yang berbeda
Saya tidak bisa setuju dengan Anda. Jika dua objek memiliki kode hash yang sama, itu tidak harus berarti bahwa mereka sama.
Jika A sama dengan B maka A.hashcode harus sama dengan B.hascode
tapi
jika A.hashcode sama dengan B.hascode, itu tidak berarti bahwa A harus sama dengan B
Jika (A != B) and (A.hashcode() == B.hashcode()), itulah yang kami sebut tabrakan fungsi hash. Itu karena kode fungsi hash selalu terbatas, sedangkan domainnya biasanya tidak. Semakin besar kode domain, semakin jarang tabrakan terjadi. Fungsi hash yang baik harus mengembalikan hash yang berbeda untuk objek yang berbeda dengan kemungkinan terbesar yang dapat dicapai mengingat ukuran kode domain tertentu. Ini jarang bisa sepenuhnya dijamin.
Krzysztof Jabłoński
Ini seharusnya hanya komentar untuk posting di atas untuk Gray. Informasi yang baik tetapi tidak benar-benar menjawab pertanyaan
Christopher Rucinski
Komentar yang bagus tetapi berhati-hatilah dalam menggunakan istilah 'objek berbeda' ... karena equals () dan implementasi hashCode () tidak harus mengenai objek yang berbeda dalam konteks OO tetapi biasanya lebih tentang representasi model domain mereka (misalnya, dua orang dapat dianggap sama jika mereka berbagi kode negara dan ID negara - meskipun ini mungkin dua 'objek' yang berbeda dalam JVM - mereka dianggap 'sama' dan memiliki kode hash yang diberikan) ...
Darrell Teague
7
Jika Anda menggunakan gerhana, Anda dapat membuat equals()dan hashCode()menggunakan:
Sumber -> Hasilkan kode hash () dan equals ().
Dengan menggunakan fungsi ini, Anda dapat memutuskan bidang mana yang ingin Anda gunakan untuk perhitungan kode persamaan dan hash, dan Eclipse menghasilkan metode yang sesuai.
Kelemahan dari API ini adalah Anda membayar biaya konstruksi objek setiap kali Anda memanggil sama dan kode hash (kecuali objek Anda tidak dapat diubah dan Anda precompute hash), yang dapat banyak dalam kasus-kasus tertentu.
James McMahon
ini adalah pendekatan favorit saya, hingga saat ini. Saya telah berlari ke StackOverFlowError saat menggunakan kriteria untuk asosiasi SharedKey OneToOne. Terlebih lagi, Objectskelas menyediakan hash(Object ..args)& equals()metode dari Java7 pada. Ini direkomendasikan untuk aplikasi apa pun yang menggunakan jdk 1.7+
Diablo
@Diablo Saya kira, masalah Anda adalah siklus dalam grafik objek dan kemudian Anda kurang beruntung dengan sebagian besar implementasi karena Anda perlu mengabaikan beberapa referensi atau untuk memutus siklus (mandat sebuah IdentityHashMap). FWIW Saya menggunakan kode hash berbasis id dan sama dengan untuk semua entitas.
maaartinus
6
Hanya catatan singkat untuk melengkapi jawaban lain yang lebih terperinci (dalam hal kode):
Jika saya memahami pertanyaan Anda dengan benar, Anda memiliki kelas koleksi khusus (yaitu kelas baru yang meluas dari antarmuka Koleksi) dan Anda ingin menerapkan metode hashCode ().
Jika kelas koleksi Anda memperluas AbstractList, maka Anda tidak perlu khawatir tentang hal itu, sudah ada implementasi equals () dan hashCode () yang berfungsi dengan mengiterasi semua objek dan menambahkan kode hash mereka () bersama-sama.
Sekarang jika yang Anda inginkan adalah cara terbaik untuk menghitung kode hash untuk kelas tertentu, saya biasanya menggunakan operator ^ (bitwise eksklusif atau) untuk memproses semua bidang yang saya gunakan dalam metode equals:
(Bisakah Anda mendapatkan kode hash langsung dari int di Jawa hari ini? Saya pikir ini melakukan autocasting .. jika itu masalahnya, lewati toString, itu jelek.)
bug ada dalam jawaban panjang sekitar about8.blogspot.com - mendapatkan kode hash dari rangkaian string membuat Anda memiliki fungsi hash yang sama untuk setiap kombinasi string yang menambahkan hingga string yang sama.
SquareCog
1
Jadi ini meta-diskusi dan tidak terkait dengan pertanyaan sama sekali? ;-)
Huppie
1
Ini merupakan koreksi terhadap jawaban yang diajukan yang memiliki cacat yang cukup signifikan.
SquareCog
Ini adalah implementasi yang sangat terbatas
Christopher Rucinski
Implementasi Anda menghindari masalah dan memperkenalkan yang lain; Bertukar foodan barmengarah ke hal yang sama hashCode. Anda toStringAFAIK tidak mengkompilasi, dan jika tidak, maka itu mengerikan tidak efisien. Sesuatu seperti 109 * getFoo().hashCode() + 57 * getBar().hashCode()lebih cepat, lebih sederhana dan tidak menghasilkan benturan yang tidak perlu.
maaartinus
2
Saat Anda secara spesifik meminta koleksi, saya ingin menambahkan aspek yang belum dijawab oleh jawaban lain: HashMap tidak mengharapkan kunci mereka untuk mengubah kode hash mereka begitu ditambahkan ke koleksi. Akan mengalahkan seluruh tujuan ...
Saya lebih suka menggunakan metode utilitas dari Google Koleksi Google lib dari Objek kelas yang membantu saya menjaga kode saya bersih. Sangat sering equalsdan hashcodemetode dibuat dari template IDE, sehingga tidak bersih untuk dibaca.
Berikut ini adalah demonstrasi pendekatan JDK 1.7+ lainnya dengan logika superclass. Saya melihatnya cukup meyakinkan dengan kelas Object hashCode () dicatat, ketergantungan JDK murni dan tidak ada pekerjaan manual tambahan. Harap dicatat Objects.hash()tidak ada toleransi.
Saya belum memasukkan equals()implementasi tetapi pada kenyataannya Anda tentu saja akan membutuhkannya.
import java.util.Objects;publicclassDemo{publicstaticclass A {privatefinalString param1;public A(finalString param1){this.param1 = param1;}@Overridepublicint hashCode(){returnObjects.hash(super.hashCode(),this.param1);}}publicstaticclass B extends A {privatefinalString param2;privatefinalString param3;public B(finalString param1,finalString param2,finalString param3){super(param1);this.param2 = param2;this.param3 = param3;}@Overridepublicfinalint hashCode(){returnObjects.hash(super.hashCode(),this.param2,this.param3);}}publicstaticvoid main(String[] args){
A a =new A("A");
B b =new B("A","B","C");System.out.println("A: "+ a.hashCode());System.out.println("B: "+ b.hashCode());}}
memiliki yang sama hashCode, yaitu 31*(a+b) + csebagai pengganda yang digunakan untukList.hashCode kembali di sini. Jelas, tabrakan tidak dapat dihindari, tetapi menghasilkan tabrakan yang tidak perlu hanya ... tidak perlu.
Tidak ada yang secara substansial pintar dalam menggunakan 31. Pengganda harus ganjil untuk menghindari kehilangan informasi (pengganda genap mana pun kehilangan setidaknya bit yang paling signifikan, kelipatan empat kehilangan dua, dll.). Pengganda ganjil dapat digunakan. Pengganda kecil dapat menyebabkan komputasi lebih cepat (JIT dapat menggunakan shift dan penambahan), tetapi mengingat bahwa perkalian memiliki latensi hanya tiga siklus pada Intel / AMD modern, ini hampir tidak masalah. Pengganda kecil juga menyebabkan lebih banyak tabrakan untuk input kecil, yang terkadang menjadi masalah.
Menggunakan prime tidak ada gunanya karena bilangan prima tidak memiliki makna di cincin Z / (2 ** 32).
Jadi, saya akan merekomendasikan menggunakan nomor ganjil besar yang dipilih secara acak (jangan ragu untuk mengambil prime) Karena CPU i86 / amd64 dapat menggunakan instruksi yang lebih pendek untuk pemasangan operan dalam byte bertanda tunggal, ada keunggulan kecepatan kecil untuk pengganda seperti 109. Untuk meminimalkan benturan, ambil sesuatu seperti 0x58a54cf5.
Menggunakan pengganda yang berbeda di tempat yang berbeda itu membantu, tetapi mungkin tidak cukup untuk membenarkan pekerjaan tambahan.
Saat menggabungkan nilai hash, saya biasanya menggunakan metode menggabungkan yang digunakan di pustaka boost c ++, yaitu:
seed ^= hasher(v)+0x9e3779b9+(seed<<6)+(seed>>2);
Ini melakukan pekerjaan yang cukup baik untuk memastikan distribusi yang merata. Untuk beberapa diskusi tentang cara kerja rumus ini, lihat posting StackOverflow: Nomor ajaib di boost :: hash_combine
Untuk kelas sederhana, seringkali paling mudah untuk mengimplementasikan kode hash () berdasarkan bidang kelas yang diperiksa oleh implementasi equals ().
Yang paling penting adalah menjaga agar kode hash () dan equals () konsisten: jika equals () mengembalikan nilai true untuk dua objek, maka kode hash () harus mengembalikan nilai yang sama. Jika equals () mengembalikan false, maka hashCode () harus mengembalikan nilai yang berbeda.
Seperti SquareCog sudah perhatikan. Jika kode hash yang dihasilkan sekali dari gabungan dari dua string adalah sangat mudah untuk menghasilkan massa tabrakan: ("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc"). Ini cacat parah. Akan lebih baik untuk mengevaluasi kode hash untuk kedua bidang dan kemudian menghitung kombinasi linear dari keduanya (lebih disukai menggunakan bilangan prima sebagai koefisien).
Krzysztof Jabłoński
@ KrzysztofJabłoński Benar. Selain itu, bertukar foodan barmenghasilkan tabrakan yang tidak perlu juga.
Objects.hashCode(collection)
seharusnya menjadi solusi yang sempurna!collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… )Jawaban:
Implementasi terbaik? Itu adalah pertanyaan yang sulit karena itu tergantung pada pola penggunaan.
A untuk hampir semua kasus, implementasi yang baik dan wajar diusulkan dalam Josh Bloch 's Java yang Efektif pada Butir 8 (edisi kedua). Yang terbaik adalah mencarinya di sana karena penulis menjelaskan di sana mengapa pendekatannya bagus.
Versi singkat
Buat
int result
dan tetapkan nilai yang bukan nol .Untuk setiap bidang yang
f
diuji dalamequals()
metode ini, hitung kode hashc
dengan:boolean
: hitung(f ? 0 : 1)
;byte
,char
,short
atauint
: menghitung(int)f
;long
: hitung(int)(f ^ (f >>> 32))
;float
: hitungFloat.floatToIntBits(f)
;double
: menghitungDouble.doubleToLongBits(f)
dan menangani nilai pengembalian seperti setiap nilai panjang;hashCode()
metode atau 0 jikaf == null
;Gabungkan nilai hash
c
denganresult
:Kembali
result
Ini harus menghasilkan distribusi nilai hash yang tepat untuk sebagian besar situasi penggunaan.
sumber
Jika Anda senang dengan implementasi Java Efektif yang direkomendasikan oleh dmeister, Anda dapat menggunakan panggilan perpustakaan alih-alih memutar sendiri:
Ini membutuhkan Guava (
com.google.common.base.Objects.hashCode
) atau pustaka standar di Java 7 (java.util.Objects.hash
) tetapi bekerja dengan cara yang sama.sumber
hashCode
adalah jika Anda memiliki kebiasaanequals
, dan itulah tepatnya metode perpustakaan ini dirancang untuk. Dokumentasi ini cukup jelas tentang perilaku mereka terkaitequals
. Implementasi perpustakaan tidak mengklaim untuk membebaskan Anda dari mengetahui apa karakteristikhashCode
implementasi yang benar - perpustakaan ini memudahkan Anda untuk mengimplementasikan implementasi yang sesuai untuk sebagian besar kasus di manaequals
ditimpa.java.util.Objects.hash(...)
metode JDK7 daripadacom.google.common.base.Objects.hashCode(...)
metode jambu biji . Saya pikir kebanyakan orang akan memilih perpustakaan standar daripada ketergantungan ekstra.hashCode()
untuk array hanya itujava.lang.System.identityHashCode(...)
.Lebih baik menggunakan fungsi yang disediakan oleh Eclipse yang melakukan pekerjaan yang cukup bagus dan Anda dapat menempatkan upaya dan energi Anda dalam mengembangkan logika bisnis.
sumber
Meskipun ini terkait dengan
Android
dokumentasi (Mesin Wayback) dan kode saya sendiri di Github , ini akan berfungsi untuk Java secara umum. Jawaban saya adalah perpanjangan dari Jawaban dmeister dengan hanya kode yang lebih mudah dibaca dan dimengerti.EDIT
Biasanya, saat Anda menimpa
hashcode(...)
, Anda juga ingin menimpaequals(...)
. Jadi bagi yang mau atau sudah menerapkanequals
, berikut ini adalah referensi yang bagus dari Github saya ...sumber
Pertama-tama pastikan bahwa persamaan diterapkan dengan benar. Dari artikel IBM DeveloperWorks :
Kemudian pastikan bahwa hubungannya dengan hashCode menghormati kontak (dari artikel yang sama):
Akhirnya fungsi hash yang baik harus berusaha untuk mendekati fungsi hash yang ideal .
sumber
about8.blogspot.com, katamu
Saya tidak bisa setuju dengan Anda. Jika dua objek memiliki kode hash yang sama, itu tidak harus berarti bahwa mereka sama.
Jika A sama dengan B maka A.hashcode harus sama dengan B.hascode
tapi
jika A.hashcode sama dengan B.hascode, itu tidak berarti bahwa A harus sama dengan B
sumber
(A != B) and (A.hashcode() == B.hashcode())
, itulah yang kami sebut tabrakan fungsi hash. Itu karena kode fungsi hash selalu terbatas, sedangkan domainnya biasanya tidak. Semakin besar kode domain, semakin jarang tabrakan terjadi. Fungsi hash yang baik harus mengembalikan hash yang berbeda untuk objek yang berbeda dengan kemungkinan terbesar yang dapat dicapai mengingat ukuran kode domain tertentu. Ini jarang bisa sepenuhnya dijamin.Jika Anda menggunakan gerhana, Anda dapat membuat
equals()
danhashCode()
menggunakan:Dengan menggunakan fungsi ini, Anda dapat memutuskan bidang mana yang ingin Anda gunakan untuk perhitungan kode persamaan dan hash, dan Eclipse menghasilkan metode yang sesuai.
sumber
Ada implementasi yang baik dari Jawa Efektif 's
hashcode()
danequals()
logika dalam Apache Commons Lang . Lihat HashCodeBuilder dan EqualsBuilder .sumber
Objects
kelas menyediakanhash(Object ..args)
&equals()
metode dari Java7 pada. Ini direkomendasikan untuk aplikasi apa pun yang menggunakan jdk 1.7+IdentityHashMap
). FWIW Saya menggunakan kode hash berbasis id dan sama dengan untuk semua entitas.Hanya catatan singkat untuk melengkapi jawaban lain yang lebih terperinci (dalam hal kode):
Jika saya mempertimbangkan pertanyaan bagaimana-membuat-saya-membuat-tabel- has -di-java dan terutama entri FAQ jGuru , saya percaya beberapa kriteria lain yang dapat dinilai oleh kode hash adalah:
sumber
Jika saya memahami pertanyaan Anda dengan benar, Anda memiliki kelas koleksi khusus (yaitu kelas baru yang meluas dari antarmuka Koleksi) dan Anda ingin menerapkan metode hashCode ().
Jika kelas koleksi Anda memperluas AbstractList, maka Anda tidak perlu khawatir tentang hal itu, sudah ada implementasi equals () dan hashCode () yang berfungsi dengan mengiterasi semua objek dan menambahkan kode hash mereka () bersama-sama.
Sekarang jika yang Anda inginkan adalah cara terbaik untuk menghitung kode hash untuk kelas tertentu, saya biasanya menggunakan operator ^ (bitwise eksklusif atau) untuk memproses semua bidang yang saya gunakan dalam metode equals:
sumber
@ about8: ada bug yang cukup serius di sana.
kode hash yang sama
Anda mungkin menginginkan sesuatu seperti
(Bisakah Anda mendapatkan kode hash langsung dari int di Jawa hari ini? Saya pikir ini melakukan autocasting .. jika itu masalahnya, lewati toString, itu jelek.)
sumber
foo
danbar
mengarah ke hal yang samahashCode
. AndatoString
AFAIK tidak mengkompilasi, dan jika tidak, maka itu mengerikan tidak efisien. Sesuatu seperti109 * getFoo().hashCode() + 57 * getBar().hashCode()
lebih cepat, lebih sederhana dan tidak menghasilkan benturan yang tidak perlu.Saat Anda secara spesifik meminta koleksi, saya ingin menambahkan aspek yang belum dijawab oleh jawaban lain: HashMap tidak mengharapkan kunci mereka untuk mengubah kode hash mereka begitu ditambahkan ke koleksi. Akan mengalahkan seluruh tujuan ...
sumber
Gunakan metode refleksi pada Apache Commons EqualsBuilder dan HashCodeBuilder .
sumber
Saya menggunakan pembungkus kecil di sekitar
Arrays.deepHashCode(...)
karena menangani array yang disediakan sebagai parameter dengan benarsumber
metode hashing apa pun yang mendistribusikan nilai hash secara merata pada rentang yang mungkin adalah implementasi yang baik. Lihat java efektif ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result ), ada tip yang baik di sana untuk implementasi kode hash (item 9 saya pikir ...).
sumber
Saya lebih suka menggunakan metode utilitas dari Google Koleksi Google lib dari Objek kelas yang membantu saya menjaga kode saya bersih. Sangat sering
equals
danhashcode
metode dibuat dari template IDE, sehingga tidak bersih untuk dibaca.sumber
Berikut ini adalah demonstrasi pendekatan JDK 1.7+ lainnya dengan logika superclass. Saya melihatnya cukup meyakinkan dengan kelas Object hashCode () dicatat, ketergantungan JDK murni dan tidak ada pekerjaan manual tambahan. Harap dicatat
Objects.hash()
tidak ada toleransi.Saya belum memasukkan
equals()
implementasi tetapi pada kenyataannya Anda tentu saja akan membutuhkannya.sumber
Implementasi standar lemah dan menggunakannya menyebabkan tabrakan yang tidak perlu. Bayangkan a
Sekarang,
dan
memiliki yang sama
hashCode
, yaitu31*(a+b) + c
sebagai pengganda yang digunakan untukList.hashCode
kembali di sini. Jelas, tabrakan tidak dapat dihindari, tetapi menghasilkan tabrakan yang tidak perlu hanya ... tidak perlu.Tidak ada yang secara substansial pintar dalam menggunakan
31
. Pengganda harus ganjil untuk menghindari kehilangan informasi (pengganda genap mana pun kehilangan setidaknya bit yang paling signifikan, kelipatan empat kehilangan dua, dll.). Pengganda ganjil dapat digunakan. Pengganda kecil dapat menyebabkan komputasi lebih cepat (JIT dapat menggunakan shift dan penambahan), tetapi mengingat bahwa perkalian memiliki latensi hanya tiga siklus pada Intel / AMD modern, ini hampir tidak masalah. Pengganda kecil juga menyebabkan lebih banyak tabrakan untuk input kecil, yang terkadang menjadi masalah.Menggunakan prime tidak ada gunanya karena bilangan prima tidak memiliki makna di cincin Z / (2 ** 32).
Jadi, saya akan merekomendasikan menggunakan nomor ganjil besar yang dipilih secara acak (jangan ragu untuk mengambil prime) Karena CPU i86 / amd64 dapat menggunakan instruksi yang lebih pendek untuk pemasangan operan dalam byte bertanda tunggal, ada keunggulan kecepatan kecil untuk pengganda seperti 109. Untuk meminimalkan benturan, ambil sesuatu seperti 0x58a54cf5.
Menggunakan pengganda yang berbeda di tempat yang berbeda itu membantu, tetapi mungkin tidak cukup untuk membenarkan pekerjaan tambahan.
sumber
Saat menggabungkan nilai hash, saya biasanya menggunakan metode menggabungkan yang digunakan di pustaka boost c ++, yaitu:
Ini melakukan pekerjaan yang cukup baik untuk memastikan distribusi yang merata. Untuk beberapa diskusi tentang cara kerja rumus ini, lihat posting StackOverflow: Nomor ajaib di boost :: hash_combine
Ada diskusi bagus tentang berbagai fungsi hash di: http://burtleburtle.net/bob/hash/doobs.html
sumber
Untuk kelas sederhana, seringkali paling mudah untuk mengimplementasikan kode hash () berdasarkan bidang kelas yang diperiksa oleh implementasi equals ().
Yang paling penting adalah menjaga agar kode hash () dan equals () konsisten: jika equals () mengembalikan nilai true untuk dua objek, maka kode hash () harus mengembalikan nilai yang sama. Jika equals () mengembalikan false, maka hashCode () harus mengembalikan nilai yang berbeda.
sumber
("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc")
. Ini cacat parah. Akan lebih baik untuk mengevaluasi kode hash untuk kedua bidang dan kemudian menghitung kombinasi linear dari keduanya (lebih disukai menggunakan bilangan prima sebagai koefisien).foo
danbar
menghasilkan tabrakan yang tidak perlu juga.