2013-04-12 16 views
5

jeśli dobrze pamiętam domyślną implementacją hashCode() w java obiektu typu Object() jest zwrócenie adresu pamięci obiektu. Kiedy tworzymy własne klasy, czytałem, że chcemy przesłonić hashCode(), aby po włożeniu ich do kolekcji związanej z hashem, takiej jak HashMap(), będzie działać poprawnie. Ale dlaczego adres pamięci jest zły?Dlaczego domyślny hashcode() w java bad jest zły?

Oczywiście na pewno zabraknie pamięci i będziesz miał kolizje, ale jedynym przypadkiem, w którym widzę, że jest to problem, jest sytuacja, w której masz do czynienia z TONAMI danych i masz bardzo mało pamięci, a następnie START wpływać na wydajność, ponieważ zbiory związane z hashem w kolizjach rozstrzygających java są łączone łańcuchami (zasobnik zostanie połączony z listą wartości, które zostały rozwiązane na ten sam kod/indeks).

+3

Nie masz problemu. Możesz zacząć czytać [this] (http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java?rq=1). Problemu nie można postrzegać niezależnie od koncepcji równości. –

+1

Nie ma w tym nic złego, czasami trzeba to zmienić z innych powodów. – Affe

+0

Domyślny 'hashCode()' nie ma nic wspólnego z adresami pamięci. Dokumentacja wymienia adresy pamięci jako możliwe dane wejściowe do obliczania kodu skrótu, ale nie jest to sposób implementacji w dowolnej najnowszej maszynie JVM. – Holger

Odpowiedz

0

Podczas gdy nie używasz metody equals, nie można zdefiniować hashCode, ale po tym kontrakcie hashCode ma dać taki sam wynik dla obiektów, który jest równy. Nie możesz być pewien, że będzie, więc trzeba go przerobić siebie

hashCode

20

Domyślna implementacja działa dobrze, jeśli każdy przedmiot jest unikatowy. Ale jeśli nadpisujesz equals(), wówczas domyślnie mówisz, że obiekty o różnych adresach mogą być równoważne sobie nawzajem. W takim przypadku musisz również przesłonić hashCode().

Pomyśl o klasie String.

String s1 = new String("foo"); 
String s2 = new String("foo"); 

Te dwa ciągi są równe i dlatego ich kody skrótów muszą być równe. Ale są to odrębne obiekty z różnymi adresami.

s1 == s2   // false, different addresses 
s1.equals(s2) // true, same contents 

Używanie ich adresów jako kodów mieszania byłoby błędem. Dlatego String zastępuje hashCode(), aby zapewnić, że równe łańcuchy mają równe kody skrótu. To pomaga spotkać hashCode() 's kontrakt, który jest:

Jeśli a.equals(b) jest prawdą, to a.hashCode() == b.hashCode().

Konkluzja: Jeśli zastępują equals(), a także zastępują hashCode().

+0

dzięki, wygląda na to, że wszystko zależy od tego, jak zdefiniujesz równość. W podanym przykładzie równość została określona na podstawie ich wartości łańcuchowych, więc użycie funkcji hash Object() byłoby złe, ponieważ w twoim hashmapie byłyby 2 różne wpisy, chociaż są one takie same. Sprawdziłem, jak Java zaimplementowała hashCode() dla napisu i znalazłem to: http://java-bytes.blogspot.com/2009/10/hashcode-of-string-in-java.html; czy ktoś chciałby to wyjaśnić lub w ogóle zastąpić hashCode()? Wszystkie przykłady, które widziałem w Internecie wyglądają na skomplikowane. –

+0

Efektywne Jawa autorstwa Josh Blocha obejmuje tę studnię i zawiera strategie implementacji. Ides również wygeneruje dla ciebie rozsądne wdrożenia. Na przykład klasa pracownika może być jednoznacznie zidentyfikowana przez jej pracownika. W takim przypadku użyjesz go do równości i mieszania. Josh Bloch sugeruje użycie pewnej techniki do rozprzestrzeniania blisko powiązanych liczb (sprawienie, że hasz będzie bardziej wydajny (podstawowe książki java wyjaśniają, dlaczego to jest dobre) .Jeśli klasa jest unikatowa dla wielu pól (klucz złożony), to każdy z nich powinien być użyty w metody równości i haszowania – Romski

+0

Wierzę, że przykład łańcucha "foo" nie jest dokładny, ponieważ Java interns String literałów i stałych, więc s1 == s2 jest prawdziwe, nawet jeśli są one różne obiekty – ChaimKut

1

Dokładne obiekty kluczowe używane podczas umieszczania obiektów w mapie nie są dostępne, aby uzyskać dostęp do mapy w dalszej części programu. A więc skończysz na przecenianiu metody równości. Kiedy nadpisujesz metodę równości, upewniasz się, że ich kody hashowe są równe, w przeciwnym razie nie możesz odzyskać obiektów z mapy mieszającej.

0

Podejmowanie decyzji, która implementacja hashCode() powinna być używana przez strukturę danych, jest nieodłącznie częścią interfejsu API funkcji hashmap.

Zapewnienie pewnych API informacji, które nie są ściśle obsługiwane przez Ciebie, przejmuje kontrolę z rąk.

Korzystanie domyślny hashCode() realizacja pary klawiszy że chcesz zarządzać na adres pamięci, który nie jest obsługiwany przez ciebie, a ty nie masz żadnej kontroli nad.

Powiedz, że pewnego dnia będziesz chciał użyć określonego optymalizatora pamięci, który przenosi obiekty wokół pamięci w celu uzyskania spójności i lepszej wydajności. Nie będziesz już mógł używać swojej struktury danych, ponieważ zawiera oryginalne zakodowane adresy kluczy.

W bardziej praktycznego punktu widzenia,

W celu odzyskania wartości ze struktury danych w całym programie, trzeba będzie zachować odniesień do wszystkich klawiszy, które zostały wcześniej wstawionych (przez przy użyciu innej struktury danych). Nie będzie można używać kluczy, które są "podobne" pod względem stanu obiektu.

Person steve1 = new Person("Steve","21","engineer"); 
Person steve2 = new Person("Steve","21","engineer"); 

map.put(steve1,"great worker"); 

map.get(steve2); 
// returns null because "steve2" is not considered a key like "steve1" 

map.get(steve1); 
// returns "great worker" 
+1

Twoja odpowiedź jest niepoprawna.Hash zostaje zbuforowany, a obiekt zachowuje swój domyślny hasz przez cały okres istnienia obiektu Część GC redystrybuuje pamięć, więc zmiany adresu nie są rzadkie, a ponieważ hashCode nigdy nie powinien się zmieniać e, hashCode nie może trywialnie zwrócić adresu. – Andy

Powiązane problemy