Jawaban nyata untuk
mengapa ada Comparatorantarmuka tetapi tidak Hasherdan 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/ Comparatordiperkenalkan 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 Objectmemilikinya, dan mereka memiliki tinggal di sana secara fisik untuk memberikan kompatibilitas kode terkompilasi (JVM) juga - dan menambahkan antarmuka eksplisit untuk setiap Objectsubkelas yang benar-benar menerapkannya akan membuat kekacauan ini sama (sic!) dengan Clonablesatu ( 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 Hashtabledengan 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 equalsdan hashCodedideklarasikan 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- equalskoneksi 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 Hashtableharus secara eksplisit mengimplementasikan beberapa antarmuka (interface dan masih hanya di awal mereka saat itu ... tidak ada Comparablebelum bahkan!) , membuat ini jera untuk menggunakannya untuk banyak - atau Objectharus 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 Objectjuga.
Karena implementasi default dari metode-metode tersebut pada valid a& b Objects pada dasarnya tidak berguna dengan menjadi berlebihan (membuat a.equals(b) sama dengan a==bdan a.hashCode() == b.hashCode() kira-kira sama dengan a==bjuga, kecuali hashCodedan / atau equalsditimpa, atau Anda GC ratusan ribu Objects 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 IHashableantarmuka. 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#hashCodemasih 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.
Personmenerapkan perilakuequalsdan 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. SeluruhObjecttipe Java adalah gabungan dari tanggung jawab yang berbeda - hanyagetClass,finalizedantoStringmetode yang tampaknya dapat dibenarkan dari praktik terbaik saat ini.