Najlepsze wdrożenie? To trudne pytanie, ponieważ zależy od wzorca użytkowania.
A dla prawie wszystkich przypadkach rozsądnym dobrej realizacji został zaproponowany w Josh Bloch „s Effective Java w punkcie 8 (wydanie drugie). Najlepiej jest to sprawdzić, ponieważ autor wyjaśnia, dlaczego takie podejście jest dobre.
Krótka wersja
Utwórz a int resulti przypisz wartość niezerową .
Dla każdego polaf testowanego w equals()metodzie oblicz kod skrótu cpoprzez:
Jeśli pole f to boolean: oblicz (f ? 0 : 1);
Jeśli pole F jest byte, char, shorti int: oblicz (int)f;
Jeśli pole f to long: oblicz (int)(f ^ (f >>> 32));
Jeśli pole f to float: oblicz Float.floatToIntBits(f);
Jeśli pole f jest double: oblicz Double.doubleToLongBits(f)i zwróć wartość zwracaną jak każdą długą wartość;
Jeśli pole f jest obiektem : użyj wyniku hashCode()metody lub 0, jeślif == null ;
Jeśli pole f jest tablicą : zobacz każde pole jako osobny element i oblicz wartość skrótu w sposób rekurencyjny i połącz wartości w sposób opisany poniżej.
Połącz wartość skrótu cz result:
result =37* result + c
Powrót result
Powinno to doprowadzić do właściwego rozkładu wartości skrótu w większości sytuacji użycia.
Tak, jestem szczególnie ciekawy, skąd pochodzi liczba 37.
Kip
17
Użyłem pozycji 8 książki „Effective Java” Josha Blocha.
dmeister
39
@dma_k Powodem używania liczb pierwszych i metody opisanej w tej odpowiedzi jest zapewnienie unikalności obliczonego kodu skrótu . Korzystając z liczb niepierwszych, nie możesz tego zagwarantować. Nie ma znaczenia, którą liczbę pierwszą wybierzesz, nie ma nic magicznego w liczbie 37 (szkoda, że 42 nie jest liczbą pierwszą, co?)
Simon Forsberg
34
@ SimonAndréForsberg Cóż, obliczony kod skrótu nie zawsze może być unikalny :) To kod skrótu. Wpadłem jednak na pomysł: liczba pierwsza ma tylko jeden mnożnik, podczas gdy liczba pierwsza nie ma co najmniej dwóch. To tworzy dodatkową kombinację dla operatora mnożenia, aby uzyskać ten sam skrót, tzn. Spowodować kolizję.
O ile nie ma dobrego powodu, aby z nich nie korzystać, zdecydowanie należy ich używać w każdym przypadku. (Formułowanie go mocniej, ponieważ należy go sformułować.) Obowiązują typowe argumenty przemawiające za użyciem standardowych implementacji / bibliotek (najlepsze praktyki, dobrze przetestowane, mniej podatne na błędy itp.).
Kissaki
7
@ justin.hughey wydaje się, że jesteś zdezorientowany. Jedynym przypadkiem, który powinieneś zastąpić, hashCodejest posiadanie własnego equalsi właśnie do tego służą te biblioteki. Dokumentacja jest dość jasna na temat ich zachowania w stosunku do equals. Implementacja biblioteki nie twierdzi, że zwalnia cię z wiedzy o tym, jakie są cechy poprawnej hashCodeimplementacji - te biblioteki to robią ułatwiają implementację takiej zgodnej implementacji w większości przypadków, gdy equalsjest ona nadpisywana.
Bacar
6
Dla wszystkich programistów Androida, którzy patrzą na klasę java.util.Objects, została ona wprowadzona tylko w API 19, więc upewnij się, że korzystasz z KitKat lub nowszej wersji, w przeciwnym razie otrzymasz NoClassDefFoundError.
Andrew Kelly,
3
Najlepsza odpowiedź IMO, chociaż jako przykład wolałbym raczej java.util.Objects.hash(...)metodę JDK7 niż guawacom.google.common.base.Objects.hashCode(...) metodę . Myślę, że większość ludzi wybrałaby standardową bibliotekę zamiast dodatkowej zależności.
Malte Skoruppa
2
Jeśli są dwa lub więcej argumentów i jeśli którykolwiek z nich jest tablicą, wynik może być inny niż oczekiwany, ponieważ hashCode()dla tablicy jest tylko jej java.lang.System.identityHashCode(...).
starikoff
59
Lepiej jest korzystać z funkcjonalności dostarczonej przez Eclipse, która wykonuje całkiem niezłą robotę, a Ty możesz włożyć wysiłek i energię w rozwój logiki biznesowej.
+1 Dobre praktyczne rozwiązanie. Rozwiązanie dmeistera jest bardziej kompleksowe, ale zwykle próbuję zapamiętywać wartości zerowe, kiedy sam próbuję pisać kody skrótu.
Quantum7
1
+1 Zgadzam się z Quantum7, ale powiedziałbym, że naprawdę dobrze jest zrozumieć, co robi implementacja wygenerowana przez Eclipse i skąd czerpie szczegóły dotyczące jej implementacji.
jwir3
15
Przepraszamy, ale odpowiedzi dotyczące „funkcjonalności dostarczonej przez [niektóre IDE]” nie są tak naprawdę istotne w kontekście języka programowania w ogóle. Istnieją dziesiątki IDE, a to nie odpowiada na pytanie ... a mianowicie dlatego, że chodzi tu bardziej o algorytmiczne określenie i bezpośrednio związane z implementacją equals () - coś, o czym IDE nic nie będzie wiedział.
@Overridepublicint hashCode(){// Start with a non-zero constant. Prime is preferredint result =17;// Include a hash for each field.// Primatives
result =31* result +(booleanField ?1:0);// 1 bit » 32-bit
result =31* result + byteField;// 8 bits » 32-bit
result =31* result + charField;// 16 bits » 32-bit
result =31* result + shortField;// 16 bits » 32-bit
result =31* result + intField;// 32 bits » 32-bit
result =31* result +(int)(longField ^(longField >>>32));// 64 bits » 32-bit
result =31* result +Float.floatToIntBits(floatField);// 32 bits » 32-bitlong doubleFieldBits =Double.doubleToLongBits(doubleField);// 64 bits (double) » 64-bit (long) » 32-bit (int)
result =31* result +(int)(doubleFieldBits ^(doubleFieldBits >>>32));// Objects
result =31* result +Arrays.hashCode(arrayField);// var bits » 32-bit
result =31* result + referenceField.hashCode();// var bits » 32-bit (non-nullable)
result =31* result +// var bits » 32-bit (nullable) (nullableReferenceField ==null?0: nullableReferenceField.hashCode());return result;}
EDYTOWAĆ
Zazwyczaj, gdy przesłonisz hashcode(...), chcesz również przesłonić equals(...). Więc dla tych, którzy wdrożą lub już wdrożyli equals, oto dobre referencje z mojego Github ...
@Overridepublicboolean equals(Object o){// Optimization (not required).if(this== o){returntrue;}// Return false if the other object has the wrong type, interface, or is null.if(!(o instanceofMyType)){returnfalse;}MyType lhs =(MyType) o;// lhs means "left hand side"// Primitive fieldsreturn booleanField == lhs.booleanField
&& byteField == lhs.byteField
&& charField == lhs.charField
&& shortField == lhs.shortField
&& intField == lhs.intField
&& longField == lhs.longField
&& floatField == lhs.floatField
&& doubleField == lhs.doubleField
// Arrays&&Arrays.equals(arrayField, lhs.arrayField)// Objects&& referenceField.equals(lhs.referenceField)&&(nullableReferenceField ==null? lhs.nullableReferenceField ==null: nullableReferenceField.equals(lhs.nullableReferenceField));}
jeśli equals () zwraca true dla dwóch obiektów, to hashCode () powinien zwrócić tę samą wartość. Jeśli equals () zwraca false, to hashCode () powinno zwracać różne wartości
Nie mogę się z tobą zgodzić. Jeśli dwa obiekty mają ten sam kod skrótu, nie musi to oznaczać, że są one równe.
Jeśli A jest równe B, to kod skrótu A. musi być równy kodowi skrótu B.
ale
jeśli A. skrót jest równy B. kod nie oznacza, że A musi być równe B.
Jeśli (A != B) and (A.hashcode() == B.hashcode())to nazywamy kolizją funkcji skrótu. Dzieje się tak, ponieważ kodomaina funkcji skrótu jest zawsze skończona, podczas gdy jej domena zwykle nie jest. Im większa jest domena, tym rzadziej powinna wystąpić kolizja. Dobra funkcja skrótu powinna zwracać różne skróty dla różnych obiektów z największą możliwą możliwą do osiągnięcia przy danym rozmiarze domeny kodowej. Jednak rzadko można to w pełni zagwarantować.
Krzysztof Jabłoński
To powinien być tylko komentarz do powyższego postu dla Graya. Dobra informacja, ale tak naprawdę nie odpowiada na pytanie
Christopher Rucinski
Dobre komentarze, ale uważaj na użycie terminu „różne obiekty” ... ponieważ equals (), a tym samym implementacja hashCode () niekoniecznie dotyczą różnych obiektów w kontekście OO, ale zwykle bardziej reprezentują ich reprezentacje modelu domeny (np. Dwa ludzie mogą być traktowani tak samo, jeśli mają wspólny kod kraju i identyfikator kraju - chociaż mogą to być dwa różne „obiekty” w JVM - są uważani za „równych” i posiadających dany kod hash) ...
Darrell Teague
7
Jeśli używasz Eclipse, możesz wygenerować equals()i hashCode()użyć:
Źródło -> Wygeneruj hashCode () i equals ().
Za pomocą tej funkcji możesz zdecydować, które pola mają być używane do obliczania równości i kodu mieszania, a Eclipse generuje odpowiednie metody.
Minusem tego interfejsu API jest to, że ponosisz koszty budowy obiektu za każdym razem, gdy wywołujesz equals i hashcode (chyba że twój obiekt jest niezmienny i nie obliczasz skrótu wcześniej), co w niektórych przypadkach może być dużo.
James McMahon
to było moje ulubione podejście do niedawna. Natknąłem się na StackOverFlowError, używając kryteriów dla skojarzenia SharedKey OneToOne. Co więcej, Objectsklasa zapewnia hash(Object ..args)i equals()metody od Java7. Zalecane są do wszystkich aplikacji korzystających z jdk 1.7+
Diablo
@Diablo Myślę, że twoim problemem był cykl na wykresie obiektowym, a potem nie masz szczęścia z większością implementacji, ponieważ musisz zignorować jakieś odniesienie lub przerwać cykl (nakazanie IdentityHashMap). FWIW Używam hashCode na podstawie identyfikatora i jest równy dla wszystkich jednostek.
maaartinus,
6
Krótka uwaga na wypełnienie innej, bardziej szczegółowej odpowiedzi (w zakresie kodu):
Jeśli dobrze rozumiem twoje pytanie, masz niestandardową klasę kolekcji (tj. Nową klasę, która rozszerza się z interfejsu Collection) i chcesz zaimplementować metodę hashCode ().
Jeśli twoja klasa kolekcji rozszerza AbstractList, nie musisz się tym martwić, istnieje już implementacja equals () i hashCode (), która działa poprzez iterowanie wszystkich obiektów i dodawanie ich hashCodes () razem.
Teraz, jeśli to, co chcesz, jest najlepszym sposobem obliczenia kodu skrótu dla określonej klasy, zwykle używam operatora ^ (wyłączanie bitowe lub) do przetwarzania wszystkich pól, których używam w metodzie równości:
(czy w dzisiejszych czasach możesz uzyskać hashCode bezpośrednio z int w Javie? Myślę, że robi to autocasting .. jeśli tak jest, pomiń toString, to jest brzydkie.)
błąd znajduje się w długiej odpowiedzi about8.blogspot.com - pobranie kodu mieszającego z połączenia łańcuchów pozostawia funkcję haszującą, która jest taka sama dla dowolnej kombinacji ciągów, które składają się na ten sam ciąg.
SquareCog,
1
Więc to jest meta-dyskusja i wcale nie związana z pytaniem? ;-)
Huppie
1
Jest to poprawka do proponowanej odpowiedzi, która ma dość znaczącą wadę.
SquareCog
Jest to bardzo ograniczone wdrożenie
Christopher Rucinski
Twoje wdrożenie pozwala uniknąć problemu i wprowadza inny; Zamiana fooi barprowadzi do tego samego hashCode. Twój toStringAFAIK nie kompiluje się, a jeśli tak, to jest okropnie nieefektywny. Coś takiego 109 * getFoo().hashCode() + 57 * getBar().hashCode()jest szybsze, prostsze i nie powoduje zbędnych kolizji.
maaartinus,
2
Jak konkretnie poprosiłeś o kolekcje, chciałbym dodać aspekt, o którym inne odpowiedzi jeszcze nie wspomniały: HashMap nie oczekuje, że ich klucze zmienią kod skrótu po dodaniu do kolekcji. Pokonałby cały cel ...
Wolę używać metod narzędziowych z biblioteki Google Kolekcje z klas Obiektów, które pomagają mi utrzymać kod w czystości. Bardzo często equalsi hashcodemetody są wykonane z szablonu IDE, więc ich nie są czyste czytać.
Oto kolejna demonstracja podejścia JDK 1.7+ z uwzględnieniem logiki nadklasy. Widzę to jako całkiem wygodne z uwzględnioną hashCode () klasy Object, czystą zależnością JDK i bez dodatkowej pracy ręcznej. Uwaga: Objects.hash()tolerancja zerowa.
Nie wdrożyłem żadnej equals()implementacji, ale w rzeczywistości będzie to oczywiście potrzebne.
import java.util.Objects;publicclassDemo{publicstaticclass A {privatefinalString param1;public A(finalString param1){this.param1 = param1;}@Overridepublicint hashCode(){returnObjects.hash(super.hashCode(),this.param1);}}publicstaticclass B extends A {privatefinalString param2;privatefinalString param3;public B(finalString param1,finalString param2,finalString param3){super(param1);this.param2 = param2;this.param3 = param3;}@Overridepublicfinalint hashCode(){returnObjects.hash(super.hashCode(),this.param2,this.param3);}}publicstaticvoid main(String[] args){
A a =new A("A");
B b =new B("A","B","C");System.out.println("A: "+ a.hashCode());System.out.println("B: "+ b.hashCode());}}
mają to samo hashCode, mianowicie 31*(a+b) + cjako mnożnik zastosowany dlaList.hashCode zostaje ponownie użyty tutaj. Oczywiście kolizji nie da się uniknąć, ale tworzenie zbędnych kolizji jest po prostu ... niepotrzebne.
W użyciu nie ma nic mądrego 31. Mnożnik musi być nieparzysty, aby uniknąć utraty informacji (każdy parzysty mnożnik traci co najmniej najbardziej znaczący bit, wielokrotności czterech tracą dwa itd.). Można zastosować dowolny nieparzysty mnożnik. Małe mnożniki mogą prowadzić do szybszych obliczeń (JIT może używać przesunięć i dodatków), ale biorąc pod uwagę, że mnożenie ma opóźnienie tylko trzech cykli we współczesnym Intel / AMD, nie ma to większego znaczenia. Małe mnożniki prowadzą również do większej kolizji przy małych nakładach, co może czasem stanowić problem.
Używanie liczby pierwszej jest bezcelowe, ponieważ liczby pierwsze nie mają znaczenia w pierścieniu Z / (2 ** 32).
Tak więc polecam użycie losowo wybranej dużej liczby nieparzystej (nie krępuj się wziąć liczby pierwsze). Ponieważ procesory i86 / amd64 mogą używać krótszej instrukcji dla argumentów pasujących do pojedynczego podpisanego bajtu, istnieje niewielka przewaga szybkości dla multiplikatorów takich jak 109. Aby zminimalizować kolizje, weź coś takiego jak 0x58a54cf5.
Używanie różnych mnożników w różnych miejscach jest pomocne, ale prawdopodobnie niewystarczające, aby uzasadnić dodatkową pracę.
Podczas łączenia wartości skrótu zwykle używam metody łączenia stosowanej w bibliotece boost c ++, a mianowicie:
seed ^= hasher(v)+0x9e3779b9+(seed<<6)+(seed>>2);
Zapewnia to dość dobrą pracę, zapewniając równomierną dystrybucję. Aby uzyskać omówienie działania tej formuły, zobacz wpis StackOverflow: Magiczna liczba w boost :: hash_combine
Najważniejszą rzeczą jest utrzymanie spójności hashCode () i equals (): jeśli equals () zwraca true dla dwóch obiektów, to hashCode () powinno zwrócić tę samą wartość. Jeśli equals () zwraca false, to hashCode () powinno zwracać różne wartości.
Jak zauważyli już SquareCog. Jeśli kod skrótu jest generowany raz z połączenia dwóch łańcuchów, niezwykle łatwo jest wygenerować masę kolizji:("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc") . To poważna wada. Lepiej byłoby ocenić hashcode dla obu pól, a następnie obliczyć ich kombinację liniową (najlepiej używając liczb pierwszych jako współczynników).
Objects.hashCode(collection)
powinno być idealnym rozwiązaniem!collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… )Odpowiedzi:
Najlepsze wdrożenie? To trudne pytanie, ponieważ zależy od wzorca użytkowania.
A dla prawie wszystkich przypadkach rozsądnym dobrej realizacji został zaproponowany w Josh Bloch „s Effective Java w punkcie 8 (wydanie drugie). Najlepiej jest to sprawdzić, ponieważ autor wyjaśnia, dlaczego takie podejście jest dobre.
Krótka wersja
Utwórz a
int result
i przypisz wartość niezerową .Dla każdego pola
f
testowanego wequals()
metodzie oblicz kod skrótuc
poprzez:boolean
: oblicz(f ? 0 : 1)
;byte
,char
,short
iint
: oblicz(int)f
;long
: oblicz(int)(f ^ (f >>> 32))
;float
: obliczFloat.floatToIntBits(f)
;double
: obliczDouble.doubleToLongBits(f)
i zwróć wartość zwracaną jak każdą długą wartość;hashCode()
metody lub 0, jeślif == null
;Połącz wartość skrótu
c
zresult
:Powrót
result
Powinno to doprowadzić do właściwego rozkładu wartości skrótu w większości sytuacji użycia.
źródło
Jeśli jesteś zadowolony z efektywnej implementacji Java zalecanej przez dmeister, możesz użyć wywołania bibliotecznego zamiast tworzyć własne:
Wymaga to Guava (
com.google.common.base.Objects.hashCode
) lub standardowej biblioteki Java 7 (java.util.Objects.hash
), ale działa w ten sam sposób.źródło
hashCode
jest posiadanie własnegoequals
i właśnie do tego służą te biblioteki. Dokumentacja jest dość jasna na temat ich zachowania w stosunku doequals
. Implementacja biblioteki nie twierdzi, że zwalnia cię z wiedzy o tym, jakie są cechy poprawnejhashCode
implementacji - te biblioteki to robią ułatwiają implementację takiej zgodnej implementacji w większości przypadków, gdyequals
jest ona nadpisywana.java.util.Objects.hash(...)
metodę JDK7 niż guawacom.google.common.base.Objects.hashCode(...)
metodę . Myślę, że większość ludzi wybrałaby standardową bibliotekę zamiast dodatkowej zależności.hashCode()
dla tablicy jest tylko jejjava.lang.System.identityHashCode(...)
.Lepiej jest korzystać z funkcjonalności dostarczonej przez Eclipse, która wykonuje całkiem niezłą robotę, a Ty możesz włożyć wysiłek i energię w rozwój logiki biznesowej.
źródło
Chociaż jest to powiązane z
Android
dokumentacją (Wayback Machine) i Moim własnym kodem na Github , ogólnie będzie działało w Javie. Moja odpowiedź jest rozszerzeniem Odpowiedzi dmeistera z samym kodem, który jest znacznie łatwiejszy do odczytania i zrozumienia.EDYTOWAĆ
Zazwyczaj, gdy przesłonisz
hashcode(...)
, chcesz również przesłonićequals(...)
. Więc dla tych, którzy wdrożą lub już wdrożyliequals
, oto dobre referencje z mojego Github ...źródło
Najpierw upewnij się, że równość jest poprawnie zaimplementowana. Z artykułu IBM DeveloperWorks :
Następnie upewnij się, że ich związek z hashCode uwzględnia kontakt (z tego samego artykułu):
Wreszcie dobra funkcja skrótu powinna dążyć do zbliżenia się do idealnej funkcji skrótu .
źródło
about8.blogspot.com, powiedziałeś
Nie mogę się z tobą zgodzić. Jeśli dwa obiekty mają ten sam kod skrótu, nie musi to oznaczać, że są one równe.
Jeśli A jest równe B, to kod skrótu A. musi być równy kodowi skrótu B.
ale
jeśli A. skrót jest równy B. kod nie oznacza, że A musi być równe B.
źródło
(A != B) and (A.hashcode() == B.hashcode())
to nazywamy kolizją funkcji skrótu. Dzieje się tak, ponieważ kodomaina funkcji skrótu jest zawsze skończona, podczas gdy jej domena zwykle nie jest. Im większa jest domena, tym rzadziej powinna wystąpić kolizja. Dobra funkcja skrótu powinna zwracać różne skróty dla różnych obiektów z największą możliwą możliwą do osiągnięcia przy danym rozmiarze domeny kodowej. Jednak rzadko można to w pełni zagwarantować.Jeśli używasz Eclipse, możesz wygenerować
equals()
ihashCode()
użyć:Za pomocą tej funkcji możesz zdecydować, które pola mają być używane do obliczania równości i kodu mieszania, a Eclipse generuje odpowiednie metody.
źródło
Jest to realizacja dobra Efektywna Java „s
hashcode()
iequals()
logiki w Apache Commons Lang . Kasa HashCodeBuilder i EqualsBuilder .źródło
Objects
klasa zapewniahash(Object ..args)
iequals()
metody od Java7. Zalecane są do wszystkich aplikacji korzystających z jdk 1.7+IdentityHashMap
). FWIW Używam hashCode na podstawie identyfikatora i jest równy dla wszystkich jednostek.Krótka uwaga na wypełnienie innej, bardziej szczegółowej odpowiedzi (w zakresie kodu):
Jeśli zastanowię się nad pytaniem, jak-i-utworzyć-tabelę-skrót-java, a zwłaszcza wpis FAQ jGuru , uważam, że inne kryteria, na podstawie których można oceniać kod skrótu, to:
źródło
Jeśli dobrze rozumiem twoje pytanie, masz niestandardową klasę kolekcji (tj. Nową klasę, która rozszerza się z interfejsu Collection) i chcesz zaimplementować metodę hashCode ().
Jeśli twoja klasa kolekcji rozszerza AbstractList, nie musisz się tym martwić, istnieje już implementacja equals () i hashCode (), która działa poprzez iterowanie wszystkich obiektów i dodawanie ich hashCodes () razem.
Teraz, jeśli to, co chcesz, jest najlepszym sposobem obliczenia kodu skrótu dla określonej klasy, zwykle używam operatora ^ (wyłączanie bitowe lub) do przetwarzania wszystkich pól, których używam w metodzie równości:
źródło
@ about8: jest tam dość poważny błąd.
ten sam kod skrótu
prawdopodobnie chcesz coś takiego
(czy w dzisiejszych czasach możesz uzyskać hashCode bezpośrednio z int w Javie? Myślę, że robi to autocasting .. jeśli tak jest, pomiń toString, to jest brzydkie.)
źródło
foo
ibar
prowadzi do tego samegohashCode
. TwójtoString
AFAIK nie kompiluje się, a jeśli tak, to jest okropnie nieefektywny. Coś takiego109 * getFoo().hashCode() + 57 * getBar().hashCode()
jest szybsze, prostsze i nie powoduje zbędnych kolizji.Jak konkretnie poprosiłeś o kolekcje, chciałbym dodać aspekt, o którym inne odpowiedzi jeszcze nie wspomniały: HashMap nie oczekuje, że ich klucze zmienią kod skrótu po dodaniu do kolekcji. Pokonałby cały cel ...
źródło
Użyj metod refleksji w Apache Commons EqualsBuilder i HashCodeBuilder .
źródło
Używam małego opakowania,
Arrays.deepHashCode(...)
ponieważ poprawnie obsługuje tablice dostarczone jako parametryźródło
każda metoda mieszająca, która równomiernie rozkłada wartość skrótu w możliwym zakresie, jest dobrą implementacją. Zobacz skuteczną Java ( http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result ), istnieje wskazówka dobry tam do implementacji hashcode (myślę, że pozycja 9 ...).
źródło
Wolę używać metod narzędziowych z biblioteki Google Kolekcje z klas Obiektów, które pomagają mi utrzymać kod w czystości. Bardzo często
equals
ihashcode
metody są wykonane z szablonu IDE, więc ich nie są czyste czytać.źródło
Oto kolejna demonstracja podejścia JDK 1.7+ z uwzględnieniem logiki nadklasy. Widzę to jako całkiem wygodne z uwzględnioną hashCode () klasy Object, czystą zależnością JDK i bez dodatkowej pracy ręcznej. Uwaga:
Objects.hash()
tolerancja zerowa.Nie wdrożyłem żadnej
equals()
implementacji, ale w rzeczywistości będzie to oczywiście potrzebne.źródło
Standardowa implementacja jest słaba, a korzystanie z niej prowadzi do niepotrzebnych kolizji. Wyobraź sobie
Teraz,
i
mają to samo
hashCode
, mianowicie31*(a+b) + c
jako mnożnik zastosowany dlaList.hashCode
zostaje ponownie użyty tutaj. Oczywiście kolizji nie da się uniknąć, ale tworzenie zbędnych kolizji jest po prostu ... niepotrzebne.W użyciu nie ma nic mądrego
31
. Mnożnik musi być nieparzysty, aby uniknąć utraty informacji (każdy parzysty mnożnik traci co najmniej najbardziej znaczący bit, wielokrotności czterech tracą dwa itd.). Można zastosować dowolny nieparzysty mnożnik. Małe mnożniki mogą prowadzić do szybszych obliczeń (JIT może używać przesunięć i dodatków), ale biorąc pod uwagę, że mnożenie ma opóźnienie tylko trzech cykli we współczesnym Intel / AMD, nie ma to większego znaczenia. Małe mnożniki prowadzą również do większej kolizji przy małych nakładach, co może czasem stanowić problem.Używanie liczby pierwszej jest bezcelowe, ponieważ liczby pierwsze nie mają znaczenia w pierścieniu Z / (2 ** 32).
Tak więc polecam użycie losowo wybranej dużej liczby nieparzystej (nie krępuj się wziąć liczby pierwsze). Ponieważ procesory i86 / amd64 mogą używać krótszej instrukcji dla argumentów pasujących do pojedynczego podpisanego bajtu, istnieje niewielka przewaga szybkości dla multiplikatorów takich jak 109. Aby zminimalizować kolizje, weź coś takiego jak 0x58a54cf5.
Używanie różnych mnożników w różnych miejscach jest pomocne, ale prawdopodobnie niewystarczające, aby uzasadnić dodatkową pracę.
źródło
Podczas łączenia wartości skrótu zwykle używam metody łączenia stosowanej w bibliotece boost c ++, a mianowicie:
Zapewnia to dość dobrą pracę, zapewniając równomierną dystrybucję. Aby uzyskać omówienie działania tej formuły, zobacz wpis StackOverflow: Magiczna liczba w boost :: hash_combine
Dobra dyskusja na temat różnych funkcji skrótu znajduje się na stronie : http://burtleburtle.net/bob/hash/doobs.html
źródło
W przypadku prostej klasy często najłatwiej jest zaimplementować hashCode () na podstawie pól klasy sprawdzanych przez implementację equals ().
Najważniejszą rzeczą jest utrzymanie spójności hashCode () i equals (): jeśli equals () zwraca true dla dwóch obiektów, to hashCode () powinno zwrócić tę samą wartość. Jeśli equals () zwraca false, to hashCode () powinno zwracać różne wartości.
źródło
("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc")
. To poważna wada. Lepiej byłoby ocenić hashcode dla obu pól, a następnie obliczyć ich kombinację liniową (najlepiej używając liczb pierwszych jako współczynników).foo
ibar
wywołuje niepotrzebnych kolizji, too.