Gerçek cevap
neden bir Comparatorarayüz var ama hayır Hasherve Equator?
, Josh Bloch'un izniyle :
Orijinal Java API'leri, kapanış pazar penceresini karşılamak için sıkı bir süre içerisinde çok hızlı bir şekilde yapıldı. Orijinal Java ekibi inanılmaz bir iş çıkardı, ancak API'lerin tümü mükemmel değil.
Sorun diğer benzer konularda, örneğin olduğu gibi, Java'nın tarihinin sadece yatıyor .clone()vs Cloneable.
tl; Dr.
temelde tarihsel nedenlerden dolayı; Geçerli davranış / soyutlama JDK 1.0'da tanıtıldı ve daha sonra sabit değildi çünkü geri kod uyumluluğunu korumakla bunu yapmak neredeyse imkansızdı.
İlk olarak, birkaç tanınmış Java bilgisini özetleyelim:
- Java, başlangıçtan günümüze kadar gururla geriye dönük olarak uyumluydu ve eski API'lerin daha yeni sürümlerde hala desteklenmesini gerektiriyordu.
- Bu nedenle, JDK 1.0 ile tanıtılan hemen hemen her dil kurgusu günümüze gelmiştir.
Hashtable, .hashCode()& .equals()JDK 1.0’da uygulandı ( Hashtable )
Comparable/ ComparatorJDK 1.2'de ( Karşılaştırılabilir ) tanıtıldı ,
Şimdi, izler:
- neredeyse imkansız & PMS için anlamsız olduğunu
.hashCode()ve .equals()hala insanlar sonra geriye uyumluluk muhafaza örneğin çünkü superobject'e koymadan daha iyi soyutlama vardır fark ise ayrı arayüzler için her biri 1,2 oranında Java programcısı her biliyordu Objectbunları vardır ve onlar vardı Ayrıca derlenmiş kod (JVM) uyumluluğu sağlamak için fiziksel olarak orada kalmak - ve Objectonları gerçekten uygulayan her alt sınıfa açık bir arabirim eklemek bu karışıklığı bire eşit (sic!) Clonablebir hale getirecektir ( Bloch, Cloneable'in neden örneğin EJ 2'de tartışıldığı gibi. ve SO dahil birçok başka yer,)
- Sadece gelecek nesiller için sabit bir WTF kaynağına sahip olmaları için onları orada bıraktılar.
Şimdi, "bunlarla neyin Hashtablevar " diye sorabilirsiniz.
Cevap şudur: hashCode()/equals() 1995 / 1995'te çekirdek Java geliştiricilerinin sözleşmeli ve o kadar da iyi olmayan dil tasarım becerileri.
1996 - 4.3.2 tarihli Java 1.0 Dil Özelinden Alıntı, Sınıf Object, s.41:
Yöntemler equalsve (§21.7) hashCodegibi java.util.Hashtablefinansal tabloların yararı için beyan edilmiştir . Yöntem, eşittir referans, karşılaştırmaya değil, değere dayalı bir nesne eşitliği kavramını tanımlar.
(bu tam ifadenin daha sonraki sürümlerde değiştirildiğini not ediniz , alıntı The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap.yapmak : tarihsel JLS'yi okumadan doğrudan Hashtable- hashCode- equalsbağlantı yapmayı imkansız kılmak !)
Java ekibi iyi bir sözlük tarzı koleksiyon istediklerine karar verdiler ve yarattılar Hashtable(şimdiye kadar iyi fikir), ancak programcının mümkün olduğu kadar az kod / öğrenme eğrisi ile kullanabilmelerini istediler (hata! Geliyor!) - henüz [sonuçta 's JDK 1.0] hiçbir Generics'i yoktu çünkü ve, bu da demek olur her Object içine koymak Hashtableaçıkça bazı arabirimini uygulamak zorunda kalacak (hayır ... ve arayüzleri o zamanlar sadece kendi kuruluşundan hâlâ Comparablebile henüz!) Bunu birçok kişi için caydırıcı hale getirmek - ya Objectda dolaylı olarak bazı karma yöntemlerini uygulamak zorunda kalacaktı .
Açıkçası, yukarıda belirtilen nedenlerden dolayı çözüm 2 ile birlikte gittiler. Evet, şimdi yanlış olduklarını biliyoruz. ... akıllıca olmak çok kolay. kıkırdama
Şimdi, hashCode() ona sahip olan her nesnenin kendine özgü bir equals()metoda sahip olması gerekiyor - bu yüzden equals()de konulması gerektiği çok açıktı Object.
Yana varsayılan geçerli üzerinde bu yöntemlerin uygulamaları ave b Objects gereksiz kalarak esasen faydasız (yapım a.equals(b) eşit etmek a==bve a.hashCode() == b.hashCode() kabaca eşit için a==b, ayrıca sürece hashCodeve / veya equalsyüz binlerce değiştirileceğini veya GC Objectuygulamanızın ömrü boyunca s 1 ) , temel olarak bir yedekleme önlemi olarak ve kullanım kolaylığı için sağlandığını söylemek güvenlidir. Bu, o iyi bilinen gerçeğine olsun tam olarak nasıl olduğunu hep hem geçersiz .equals()& .hashCode()aslında nesneleri karşılaştırarak veya bunları karma-depolamak niyetinde olmadığını. Bunlardan sadece birini diğeri olmadan geçersiz kılmak, kodunuzu bozmanın iyi bir yoludur (kötü karşılaştırma sonuçları veya delice yüksek kova çarpışma değerleri ile) - ve kafanızı etrafa sokmak, yeni başlayanlar için sürekli bir karışıklık ve hata kaynağıdır (görmek için SO kelimesini arayın). Kendiniz için) ve daha deneyimli olanlar için sürekli sıkıntı.
Ayrıca, eşitler & karma kodu içeren C # fırsat daha iyi bir yol bit rağmen notu o, Eric Lippert'ın kendisi onlar Sun Java yıllık yaptığını C # ile hemen hemen aynı hatayı yaptığını belirten C # 'ın kuruluşundan önce :
Fakat neden her nesnenin bir karma tablosuna yerleştirilmek üzere kendi kendine hash yapması gerekiyor? Her nesnenin yapabilmesini gerektiren garip bir şey gibi görünüyor. Bence bugün tip sistemini sıfırdan yeniden tasarlarsak, karmaşanın belki de bir IHashablearayüzle farklı şekilde yapılabileceğini düşünüyorum . Ancak CLR tipi sistem tasarlandığında, genel tipler yoktu ve bu nedenle herhangi bir nesneyi depolayabilmek için genel amaçlı bir karma tablo gerekiyordu.
Tabii ki, 1Object#hashCode hala çarpışabilir, ancak bunu yapmak için biraz çaba harcar , bkz. Http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470 ve ayrıntılar için bağlantılı hata raporları; /programming/1381060/hashcode-uniqueness/1381114#1381114 bu konuyu daha derinlemesine ele alıyor.
PersonBeklenenequalsvehashCodedavranış davranışını uygulamak için her zaman sarmalayıcı nesnelerini tanıtabilirsiniz . O zaman olurduHashMap<PersonWrapper, V>. Bu, saf OOP yaklaşımının zarif olmadığı bir örnektir: bir cisimdeki her işlem o cismin bir yöntemi olarak anlam kazanmaz. Java'nın tümObjectonly - türü farklı sorumlulukların kaynaşmasıyla oluşmuşgetClass,finalizevetoStringyöntemler bugünün en iyi uygulamalar tarafından uzaktan haklı görünüyor.