Prawdziwa odpowiedź na
dlaczego istnieje Comparatorinterfejs, ale nie Hasheri Equator?
cytat dzięki uprzejmości Josha Blocha :
Oryginalne interfejsy API Java zostały wykonane bardzo szybko w krótkim terminie, aby sprostać zamykającemu się rynkowi. Oryginalny zespół Java wykonał niesamowitą robotę, ale nie wszystkie interfejsy API są idealne.
Problem leży wyłącznie w historii Javy, podobnie jak w innych podobnych sprawach, np . .clone()Vs.Cloneable
tl; dr
dzieje się tak głównie z przyczyn historycznych; Obecne zachowanie / abstrakcja zostało wprowadzone w JDK 1.0 i nie zostało później naprawione, ponieważ było to praktycznie niemożliwe z zachowaniem kompatybilności kodu wstecznego.
Najpierw podsumujmy kilka dobrze znanych faktów o Javie:
- Java od samego początku do dnia dzisiejszego była dumnie kompatybilna wstecz, wymagając, aby starsze interfejsy API były nadal obsługiwane w nowszych wersjach,
- dlatego prawie każdy konstrukt językowy wprowadzony w JDK 1.0 przetrwał do dnia dzisiejszego,
Hashtable, .hashCode()i .equals()zostały zaimplementowane w JDK 1.0, ( Hashtable )
Comparable/ Comparatorzostał wprowadzony w JDK 1.2 ( porównywalny ),
Teraz wygląda to następująco:
- praktycznie niemożliwe i bezsensowne było modernizowanie
.hashCode()i .equals()wyróżnianie interfejsów przy jednoczesnym zachowaniu kompatybilności wstecznej po tym, jak ludzie zdali sobie sprawę, że istnieją lepsze abstrakcje niż umieszczanie ich w superobjektach, ponieważ np. każdy programista Java w wersji 1.2 wiedział, że każdy Objectje ma, i mieli pozostanie tam fizycznie w celu zapewnienia kompatybilności skompilowanego kodu (JVM) - i dodanie jawnego interfejsu do każdej Objectpodklasy, która naprawdę je zaimplementowała, sprawiłoby, że ten bałagan byłby równy (sic!) do Clonablejednego ( Bloch omawia, dlaczego klonowanie jest do bani , również omówione np. w EJ 2nd i wiele innych miejsc, w tym SO),
- po prostu zostawili je tam, aby przyszłe pokolenie miało stałe źródło WTF.
Teraz możesz zapytać „co Hashtablema z tym wszystkim”?
Odpowiedź brzmi: hashCode()/ equals()kontrakt i niezbyt dobre umiejętności projektowania języka przez głównych programistów Java w latach 1995/1996.
Cytat ze specyfikacji języka Java 1.0, z 1996 r. - 4.3.2 The Class Object, str. 41:
Metody equalsi hashCodezostały zadeklarowane na korzyść java.util.Hashtabletablic mieszających, takich jak (§21.7). Metoda równa się definiuje pojęcie równości obiektu, które opiera się na porównaniu wartości, a nie odniesienia.
(uwaga dokładny ta wypowiedź została zmieniona w późniejszych wersjach, aby powiedzieć, cytat: The method hashCode is very useful, together with the method equals, in hashtables such as java.util.HashMap., uniemożliwiając, aby bezpośredni Hashtable- hashCode- equalspołączenia bez czytania JLS historycznych!)
Zespół Java zdecydował, że chce dobrej kolekcji w stylu słownika, i stworzył Hashtable(jak dotąd dobry pomysł), ale chciał, aby programista mógł go używać przy możliwie najmniejszej krzywej kodu / uczenia się (oops! Problemy z nadchodzącym!) - a ponieważ nie było jeszcze żadnych generycznych [to w końcu JDK 1.0], oznaczałoby to, że każdy Object włożony Hashtablemusiałby jawnie zaimplementować jakiś interfejs (a interfejsy były wtedy jeszcze w początkowej fazie ... Comparablenawet jeszcze!) , co zniechęca do korzystania z niego przez wiele osób - lub Objectmusiałoby niejawnie zaimplementować jakąś metodę haszującą.
Oczywiście wybrali rozwiązanie 2 z powodów przedstawionych powyżej. Tak, teraz wiemy, że się mylili. ... z perspektywy czasu łatwo być mądrym. chichot
Teraz hashCode() wymaga, aby każdy obiekt, który go posiada, musiał mieć odrębną equals()metodę - więc było całkiem oczywiste, że equals()trzeba go również wprowadzić Object.
Ponieważ domyślne implementacje tych metod na ważność ai b Objects są w zasadzie bezużyteczne, będąc zbędny (co a.equals(b) równa się a==bi a.hashCode() == b.hashCode() mniej więcej równa się a==brównież, chyba że hashCodei / lub equalsjest nadpisane, albo GC setki tysięcy Objects podczas całego cyklu życia aplikacji 1 ) , można bezpiecznie powiedzieć, że zostały one dostarczone głównie jako środek zapasowy i dla wygody użytkowania. Właśnie w ten sposób dochodzimy do dobrze znanego faktu, który zawsze zastępuje oba .equals()i .hashCode()jeśli zamierzasz faktycznie porównywać obiekty lub przechowywać je w postaci skrótów. Zastąpienie tylko jednego z nich bez drugiego jest dobrym sposobem na wkręcenie kodu (przez nikczemne porównanie wyników lub niesamowicie wysokie wartości kolizji z wiadrem) - a obejście go jest źródłem ciągłego zamieszania i błędów dla początkujących (wyszukaj SO, aby zobaczyć to dla siebie) i ciągłe uciążliwości dla bardziej doświadczonych.
Zauważ też, że chociaż C # radzi sobie z equals i hashcode w nieco lepszy sposób, sam Eric Lippert twierdzi, że popełnił prawie taki sam błąd z C #, jaki Sun zrobił z Javą na wiele lat przed początkiem C # :
Ale dlaczego miałoby tak być, że każdy obiekt powinien mieć możliwość mieszania się w celu wstawienia do tablicy skrótów? Wydaje się dziwne, że każdy obiekt musi być w stanie to zrobić. Myślę, że gdybyśmy dzisiaj przeprojektowywali system typów od nowa, mieszanie mogłoby być wykonane inaczej, być może z IHashableinterfejsem. Ale kiedy zaprojektowano system typu CLR, nie istniały żadne typy ogólne, dlatego też tablica mieszająca ogólnego przeznaczenia musiała być w stanie przechowywać dowolny obiekt.
1 oczywiście Object#hashCodenadal może się kolidować, ale zajmuje to trochę wysiłku, patrz: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6809470 i powiązane raporty błędów, aby uzyskać szczegółowe informacje; /programming/1381060/hashcode-uniqueness/1381114#1381114 obejmuje ten temat bardziej szczegółowo.
Persontej implementacji oczekiwanegoequalsihashCodezachowania. Miałbyś wtedyHashMap<PersonWrapper, V>. Jest to przykład, w którym podejście oparte na czystym OOP nie jest eleganckie: nie każda operacja na obiekcie ma sens jako metoda tego obiektu. Cała JavaObjecttyp jest amalgamatem różnych zadań - tylkogetClass,finalizeatoStringmetody wydają się uzasadnione zdalnie przez dzisiejszych najlepszych praktyk.