Gerçek cevap
neden bir Comparator
arayüz var ama hayır Hasher
ve 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
/ Comparator
JDK 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 Object
bunları vardır ve onlar vardı Ayrıca derlenmiş kod (JVM) uyumluluğu sağlamak için fiziksel olarak orada kalmak - ve Object
onları gerçekten uygulayan her alt sınıfa açık bir arabirim eklemek bu karışıklığı bire eşit (sic!) Clonable
bir 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 Hashtable
var " 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 equals
ve (§21.7) hashCode
gibi java.util.Hashtable
finansal 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
- equals
bağ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 Hashtable
açıkça bazı arabirimini uygulamak zorunda kalacak (hayır ... ve arayüzleri o zamanlar sadece kendi kuruluşundan hâlâ Comparable
bile henüz!) Bunu birçok kişi için caydırıcı hale getirmek - ya Object
da 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ı a
ve b
Object
s gereksiz kalarak esasen faydasız (yapım a.equals(b)
eşit etmek a==b
ve a.hashCode() == b.hashCode()
kabaca eşit için a==b
, ayrıca sürece hashCode
ve / veya equals
yüz binlerce değiştirileceğini veya GC Object
uygulamanı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 IHashable
arayü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.
Person
Beklenenequals
vehashCode
davranış 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ümObject
only - türü farklı sorumlulukların kaynaşmasıyla oluşmuşgetClass
,finalize
vetoString
yöntemler bugünün en iyi uygulamalar tarafından uzaktan haklı görünüyor.