Słyszałem na moich zajęciach na studiach, że HashTable
nowy wpis zostanie umieszczony w kategorii „następny dostępny”, jeśli nowy wpis klucza koliduje z innym.
W jaki sposób HashTable
nadal zwracałby poprawną wartość, gdyby ta kolizja wystąpiła podczas wywołania z powrotem za pomocą klucza kolizji?
Zakładam, że Keys
są String
typu i hashCode()
zwraca wartość domyślną wygenerowaną przez, powiedzmy, Javę.
Jeśli zaimplementuję własną funkcję haszującą i użyję jej jako części tabeli przeglądowej (tj. A HashMap
lub Dictionary
), jakie istnieją strategie radzenia sobie z kolizjami?
Widziałem nawet notatki dotyczące liczb pierwszych! Informacje nie są tak jasne z wyszukiwarki Google.
Kiedy mówiłeś o „Hash Table” umieści nowy wpis w „następnym dostępnym” segmencie, jeśli nowy klucz zderzy się z innym. ”, To mówisz o otwartej strategii adresowania dotyczącej rozwiązywania kolizji w tablicy haszującej.
Istnieje kilka strategii rozwiązywania kolizji w tabeli skrótów.
Pierwszy rodzaj dużej metody wymaga, aby klucze (lub wskaźniki do nich) były przechowywane w tabeli wraz z przypisanymi do nich wartościami, która obejmuje ponadto:
Inną ważną metodą radzenia sobie z kolizjami jest dynamiczna zmiana rozmiaru , która dodatkowo ma kilka sposobów:
EDYCJA : powyższe są zapożyczone z wiki_hash_table , gdzie powinieneś zajrzeć, aby uzyskać więcej informacji.
źródło
Istnieje wiele technik radzenia sobie z kolizjami. Wyjaśnię niektóre z nich
Łańcuch: w łańcuchu używamy indeksów tablic do przechowywania wartości. Jeśli kod skrótu drugiej wartości również wskazuje na ten sam indeks, wówczas zastępujemy tę wartość indeksu połączoną listą, a wszystkie wartości wskazujące na ten indeks są przechowywane na połączonej liście, a rzeczywisty indeks tablicy wskazuje na nagłówek połączonej listy. Ale jeśli istnieje tylko jeden kod skrótu wskazujący na indeks tablicy, wartość jest bezpośrednio przechowywana w tym indeksie. Ta sama logika jest stosowana podczas pobierania wartości. Jest to używane w Java HashMap / Hashtable, aby uniknąć kolizji.
Sondowanie liniowe: Technika ta jest używana, gdy w tabeli mamy więcej indeksów niż wartości do zapisania. Technika sondowania liniowego działa na zasadzie ciągłego zwiększania wartości, aż znajdziesz puste miejsce. Pseudo kod wygląda następująco:
Technika podwójnego haszowania: W tej technice używamy dwóch funkcji haszujących h1 (k) i h2 (k). Jeśli szczelina w h1 (k) jest zajęta, to druga funkcja haszująca h2 (k) używana do zwiększania indeksu. Pseudokod wygląda następująco:
Techniki liniowego sondowania i podwójnego haszowania są częścią otwartej techniki adresowania i mogą być używane tylko wtedy, gdy dostępnych gniazd jest więcej niż liczba elementów do dodania. Zajmuje mniej pamięci niż łańcuch, ponieważ nie ma tu żadnej dodatkowej struktury, ale jest powolny z powodu dużego ruchu, dopóki nie znajdziemy pustego gniazda. Również w technice otwartego adresowania, gdy przedmiot jest usuwany ze slotu, umieszczamy nagrobek, aby wskazać, że przedmiot został stąd usunięty, dlatego jest pusty.
Więcej informacji można znaleźć na tej stronie .
źródło
Gorąco zachęcam do przeczytania tego wpisu na blogu, który pojawił się niedawno w serwisie HackerNews: Jak działa HashMap w Javie
Krótko mówiąc, odpowiedź brzmi
źródło
W rzeczywistości nie jest to prawdą, przynajmniej w przypadku Oracle JDK ( jest to szczegół implementacji, który może różnić się między różnymi implementacjami interfejsu API). Zamiast tego każdy zasobnik zawiera połączoną listę wpisów starszych niż Java 8 oraz zrównoważone drzewo w wersji Java 8 lub nowszej.
Używa znaku,
equals()
aby znaleźć faktycznie pasujący wpis.Istnieją różne strategie postępowania w przypadku kolizji, które mają różne zalety i wady. Wpis Wikipedii dotyczący tabel skrótów daje dobry przegląd.
źródło
Hashtable
iwHashMap
jdk 1.6.0_22 autorstwa Sun / Oracle.public V get(Object key)
(ta sama wersja co powyżej). Jeśli znajdziesz dokładną wersję, w której pojawiają się te połączone listy, byłbym zainteresowany.Entry
obiektów:localEntry = localEntry.next
e = e.next
nie jest++index
. +1Aktualizacja od wersji Java 8: Java 8 używa samoczynnie równoważonego drzewa do obsługi kolizji, poprawiając najgorszy przypadek z O (n) do O (log n) na potrzeby wyszukiwania. Użycie samoczynnie równoważonego drzewa zostało wprowadzone w Javie 8 jako ulepszenie w stosunku do łączenia łańcuchowego (używanego do java 7), które używa listy połączonej i ma najgorszy przypadek O (n) do wyszukiwania (ponieważ musi przejść Lista)
Aby odpowiedzieć na drugą część twojego pytania, wstawianie odbywa się poprzez mapowanie danego elementu do podanego indeksu w podstawowej tablicy haszmapy, jednak gdy wystąpi kolizja, wszystkie elementy muszą być nadal zachowane (przechowywane w drugorzędnej strukturze danych , a nie tylko zastąpione w podstawowej tablicy). Zwykle robi się to poprzez uczynienie każdego komponentu tablicy (gniazda) drugorzędną strukturą danych (inaczej zasobnikiem), a element jest dodawany do zasobnika znajdującego się w podanym indeksie tablicy (jeśli klucz nie istnieje jeszcze w zasobniku, w w którym przypadku jest wymieniany).
Podczas wyszukiwania klucz jest haszowany do odpowiedniego indeksu tablicy i wyszukiwane jest element pasujący do (dokładnego) klucza w danym zasobniku. Ponieważ zasobnik nie musi obsługiwać kolizji (bezpośrednio porównuje klucze), rozwiązuje to problem kolizji, ale robi to kosztem konieczności wykonywania wstawiania i wyszukiwania w dodatkowej strukturze danych. Kluczową kwestią jest to, że w haszmie przechowywany jest zarówno klucz, jak i wartość, więc nawet jeśli hasz koliduje, klucze są porównywane bezpośrednio pod kątem równości (w zasobniku), dzięki czemu można je jednoznacznie zidentyfikować w zasobniku.
Obsługa kolizji przenosi najgorszą wydajność wstawiania i wyszukiwania z O (1) w przypadku braku obsługi kolizji do O (n) w celu łączenia (lista połączona jest używana jako dodatkowa struktura danych) i O (log n) dla samozrównoważonego drzewa.
Bibliografia:
źródło
Użyje metody equals, aby sprawdzić, czy klucz jest obecny nawet, a zwłaszcza jeśli w tym samym zasobniku jest więcej niż jeden element.
źródło
Ponieważ istnieje pewne zamieszanie co do tego, którego algorytmu używa Java HashMap (w implementacji Sun / Oracle / OpenJDK), tutaj odpowiednie fragmenty kodu źródłowego (z OpenJDK, 1.6.0_20, na Ubuntu):
Ta metoda (cite pochodzi z linii od 355 do 371) jest wywoływana podczas wyszukiwania wpisu w tabeli, na przykład z
get()
,containsKey()
i kilku innych. Pętla for przechodzi tutaj przez połączoną listę utworzoną przez obiekty wejściowe.Tutaj kod obiektów wejściowych (linie 691-705 + 759):
Zaraz potem przychodzi
addEntry()
metoda:Spowoduje to dodanie nowego wpisu z przodu zasobnika, z łączem do starego pierwszego wpisu (lub null, jeśli takiego nie ma). Podobnie
removeEntryForKey()
metoda przechodzi przez listę i dba o usunięcie tylko jednego wpisu, pozostawiając resztę listy nienaruszoną.Tak więc tutaj jest powiązana lista wpisów dla każdego segmentu i bardzo wątpię, czy zmieniło się to z
_20
na_22
, ponieważ było tak od 1.2.(Ten kod to (c) 1997-2007 Sun Microsystems i jest dostępny na GPL, ale do kopiowania lepiej użyj oryginalnego pliku, zawartego w src.zip w każdym JDK firmy Sun / Oracle, a także w OpenJDK.)
źródło
oto bardzo prosta implementacja tablicy skrótów w java. tylko narzędzia
put()
iget()
, ale możesz łatwo dodać, co chcesz. opiera się nahashCode()
metodzie Java, która jest implementowana przez wszystkie obiekty. możesz łatwo stworzyć własny interfejs,i jeśli chcesz, wymuś to za pomocą klawiszy.
źródło
Istnieją różne metody rozwiązywania kolizji, takie jak oddzielne łańcuchowanie, otwarte adresowanie, haszowanie Robin Hooda, haszowanie kukułki itp.
Java używa oddzielnych łańcuchów do rozwiązywania kolizji w tabelach z skrótami. Oto świetne łącze do tego, jak to się dzieje: http://javapapers.com/core-java/java-hashtable/
źródło