Jawaban nyata untuk
mengapa ada Comparator
antarmuka tetapi tidak Hasher
dan Equator
?
adalah, kutipan dari Josh Bloch :
API Java asli dilakukan dengan sangat cepat di bawah tenggat waktu yang ketat untuk memenuhi jendela pasar penutupan. Tim Java asli melakukan pekerjaan luar biasa, tetapi tidak semua API sempurna.
Masalahnya terletak semata-mata dalam sejarah Jawa, seperti dengan hal-hal lain yang serupa, misalnya .clone()
vs Cloneable
.
tl; dr
itu karena alasan historis terutama; perilaku / abstraksi saat ini diperkenalkan di JDK 1.0 dan tidak diperbaiki kemudian karena itu hampir tidak mungkin dilakukan dengan mempertahankan kompatibilitas kode mundur.
Pertama, mari kita simpulkan beberapa fakta Java yang terkenal:
- Java, dari awal hingga hari ini, dengan bangga kompatibel dengan mundur, membutuhkan API lama untuk tetap didukung dalam versi yang lebih baru,
- dengan demikian, hampir setiap konstruksi bahasa yang diperkenalkan dengan JDK 1.0 bertahan hingga hari ini,
Hashtable
, .hashCode()
& .equals()
diimplementasikan di JDK 1.0, ( Hashtable )
Comparable
/ Comparator
diperkenalkan di JDK 1.2 ( Sebanding ),
Sekarang, ini sebagai berikut:
- itu hampir mustahil & tidak masuk akal untuk retrofit
.hashCode()
& .equals()
untuk antarmuka yang berbeda sambil tetap mempertahankan kompatibilitas ke belakang setelah orang-orang menyadari ada abstraksi yang lebih baik daripada menempatkan mereka di superobject, karena misalnya masing-masing dan setiap programmer Java oleh 1,2 tahu bahwa setiap Object
memilikinya, dan mereka memiliki tinggal di sana secara fisik untuk memberikan kompatibilitas kode terkompilasi (JVM) juga - dan menambahkan antarmuka eksplisit untuk setiap Object
subkelas yang benar-benar menerapkannya akan membuat kekacauan ini sama (sic!) dengan Clonable
satu ( Bloch membahas mengapa Cloneable menyebalkan , juga dibahas dalam misalnya EJ 2 dan banyak tempat lain, termasuk SO),
- mereka hanya membiarkannya di sana agar generasi mendatang memiliki sumber WTF yang konstan.
Sekarang, Anda mungkin bertanya "apa yang terjadi Hashtable
dengan semua ini"?
Jawabannya adalah: hashCode()
/ equals()
kontrak dan keterampilan desain bahasa yang tidak begitu baik dari pengembang Java inti pada 1995/1996.
Kutipan dari Java 1.0 Language Spec, tanggal 1996 - 4.3.2 The Class Object
, hal.41:
Metode equals
dan hashCode
dideklarasikan untuk kepentingan hashtable seperti java.util.Hashtable
(§ 21.7). Metode sama dengan mendefinisikan gagasan tentang kesetaraan objek, yang didasarkan pada nilai, bukan referensi, perbandingan.
(perhatikan pernyataan ini tepat telah berubah di versi, mengatakan, kutipan: The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap.
, sehingga mustahil untuk membuat langsung Hashtable
- hashCode
- equals
koneksi tanpa membaca JLS sejarah!)
Tim Java memutuskan mereka ingin koleksi kamus-gaya yang baik, dan mereka menciptakan Hashtable
(ide bagus sejauh ini), tetapi mereka ingin programmer untuk dapat menggunakannya dengan kode sesedikit mungkin / kurva belajar (oops! Kesulitan masuk!) - dan, karena tidak ada obat generik belum [itu JDK 1.0 setelah semua], itu berarti bahwa baik setiap Object
put ke Hashtable
harus secara eksplisit mengimplementasikan beberapa antarmuka (interface dan masih hanya di awal mereka saat itu ... tidak ada Comparable
belum bahkan!) , membuat ini jera untuk menggunakannya untuk banyak - atau Object
harus implisit menerapkan beberapa metode hashing.
Jelas, mereka pergi dengan solusi 2, karena alasan yang diuraikan di atas. Yup, sekarang kita tahu mereka salah. ... mudah untuk menjadi pintar di belakang. tertawa kecil
Sekarang, hashCode()
mengharuskan setiap objek yang memilikinya harus memiliki equals()
metode yang berbeda - jadi itu cukup jelas yang equals()
harus dimasukkan Object
juga.
Karena implementasi default dari metode-metode tersebut pada valid a
& b
Object
s pada dasarnya tidak berguna dengan menjadi berlebihan (membuat a.equals(b)
sama dengan a==b
dan a.hashCode() == b.hashCode()
kira-kira sama dengan a==b
juga, kecuali hashCode
dan / atau equals
ditimpa, atau Anda GC ratusan ribu Object
s selama siklus hidup aplikasi Anda 1 ) , aman untuk mengatakan mereka disediakan terutama sebagai ukuran cadangan dan untuk kenyamanan penggunaan. Ini adalah persis bagaimana kita sampai pada fakta terkenal yang selalu menimpa keduanya .equals()
& .hashCode()
jika Anda berniat benar-benar membandingkan objek atau menyimpan hash. Mengganti hanya satu dari mereka tanpa yang lain adalah cara yang baik untuk mengacaukan kode Anda (dengan membandingkan hasil yang jahat atau nilai tabrakan bucket yang sangat tinggi) - dan menggerakkan kepala Anda di sekitarnya merupakan sumber kebingungan & kesalahan konstan untuk pemula (cari SO untuk melihat untuk Anda sendiri) dan gangguan terus-menerus ke yang lebih berpengalaman.
Juga, perhatikan bahwa meskipun C # berurusan dengan sama dengan & kode hash dalam cara yang sedikit lebih baik, Eric Lippert sendiri menyatakan bahwa mereka melakukan kesalahan yang hampir sama dengan C # yang Sun lakukan dengan Jawa tahun sebelum dimulainya C # :
Tetapi mengapa harus demikian halnya bahwa setiap objek harus dapat hash sendiri untuk dimasukkan ke dalam tabel hash? Sepertinya hal yang aneh mengharuskan setiap objek untuk dapat melakukannya. Saya pikir jika kita mendesain ulang sistem tipe dari awal hari ini, hashing mungkin dilakukan secara berbeda, mungkin dengan IHashable
antarmuka. Tetapi ketika sistem tipe CLR dirancang tidak ada tipe generik dan oleh karena itu tabel hash tujuan umum diperlukan untuk dapat menyimpan objek apa pun.
1 tentu saja, Object#hashCode
masih bisa bertabrakan, tetapi perlu sedikit usaha untuk melakukan itu, lihat: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470 dan laporan bug yang ditautkan untuk detail; /programming/1381060/hashcode-uniqueness/1381114#1381114 membahas subjek ini lebih mendalam.
Person
menerapkan perilakuequals
dan yang diharapkanhashCode
. Anda kemudian akan memilikiHashMap<PersonWrapper, V>
. Ini adalah salah satu contoh di mana pendekatan murni-OOP tidak elegan: tidak setiap operasi pada objek masuk akal sebagai metode objek itu. SeluruhObject
tipe Java adalah gabungan dari tanggung jawab yang berbeda - hanyagetClass
,finalize
dantoString
metode yang tampaknya dapat dibenarkan dari praktik terbaik saat ini.