Jeśli wiadra się zapełniają, musimy przejrzeć
bardzo długa lista linków.
I to jest w pewnym sensie pokonanie sedna.
Oto przykład, w którym mam cztery wiadra.
Do tej pory mam słonia i borsuka.
To całkiem niezła sytuacja, prawda?
Każdy element ma zero lub jeden element.
Teraz dodajemy jeszcze dwa elementy do naszego zestawu HashSet.
buckets elements
------- -------
0 elephant
1 otter
2 badger
3 cat
To też nie jest takie złe.
Każde wiadro ma tylko jeden element. Więc jeśli chcę wiedzieć, czy to zawiera pandę?
Mogę bardzo szybko spojrzeć na wiadro nr 1, a nie jest
tam i
Wiedziałem, że nie ma go w naszej kolekcji.
Jeśli chcę wiedzieć, czy zawiera kota, patrzę na wiadro
numer 3,
Znajduję kota, bardzo szybko wiem, czy jest w naszym
kolekcja.
Co jeśli dodam koala, to nie jest takie złe.
buckets elements
------- -------
0 elephant
1 otter -> koala
2 badger
3 cat
Może teraz zamiast w wiadrze numer 1 tylko patrzę
jeden element
Muszę spojrzeć na dwa.
Ale przynajmniej nie muszę patrzeć na słonia, borsuka i
kot.
Jeśli znów szukam pandy, może być tylko w wiadrze
numer 1 i
Nie muszę patrzeć na nic innego niż wydrę i
koala.
Ale teraz umieszczam aligatora w wiadrze nr 1 i możesz
zobaczyć może dokąd to zmierza.
Że jeśli wiadro numer 1 staje się coraz większe i większe
większy, to w zasadzie muszę wszystko przejrzeć
te elementy do znalezienia
coś, co powinno znajdować się w wiadrze nr 1.
buckets elements
------- -------
0 elephant
1 otter -> koala ->alligator
2 badger
3 cat
Jeśli zacznę dodawać ciągi do innych segmentów,
prawda, problem staje się coraz większy w każdym
pojedyncze wiadro.
Jak powstrzymać nasze wiadra przed zapełnieniem?
Rozwiązaniem jest tutaj
"the HashSet can automatically
resize the number of buckets."
Jest HashSet zdaje sobie sprawę, że wiadra dostają
zbyt pełne.
Traci tę zaletę tego jednego wyszukiwania
elementy.
I po prostu stworzy więcej wiader (zwykle dwa razy tak jak poprzednio) i
następnie umieść elementy w odpowiednim wiadrze.
Oto nasza podstawowa implementacja HashSet z osobnym
łańcuchy. Teraz utworzę „samozmieniający się zestaw HashSet”.
Ten HashSet zorientuje się, że wiadra są
robi się zbyt pełny i
potrzebuje więcej wiader.
loadFactor to kolejne pole w naszej klasie HashSet.
loadFactor reprezentuje średnią liczbę elementów na
wiadro,
powyżej którego chcemy zmienić rozmiar.
loadFactor to równowaga między przestrzenią a czasem.
Jeśli wiadra się przepełnią, zmienimy rozmiar.
To oczywiście wymaga czasu, ale
może zaoszczędzić nam czasu na drodze, jeśli łyżki to
trochę bardziej pusty.
Zobaczmy przykład.
Oto HashSet, do tej pory dodaliśmy cztery elementy.
Słoń, pies, kot i ryba.
buckets elements
------- -------
0
1 elephant
2 cat ->dog
3 fish
4
5
W tym momencie zdecydowałem, że loadFactor,
próg,
średnia liczba elementów na wiadro, które są w porządku
z wynosi 0,75.
Liczba segmentów to buckets.length, czyli 6, i
w tym momencie nasz HashSet ma cztery elementy, więc
obecny rozmiar to 4.
Zmienimy rozmiar naszego zestawu HashSet, czyli dodamy więcej wiader,
gdy średnia liczba elementów na wiadro przekracza
loadFactor.
To jest, gdy bieżący rozmiar podzielony przez segmenty. Długość wynosi
większy niż loadFactor.
W tym momencie średnia liczba elementów na wiadro
to 4 podzielone przez 6.
4 elementy, 6 wiader, czyli 0,67.
To mniej niż próg, który ustawiłem na 0,75, więc jesteśmy
w porządku.
Nie musimy zmieniać rozmiaru.
Ale teraz załóżmy, że dodajemy świstaka.
buckets elements
------- -------
0
1 elephant
2 woodchuck-> cat ->dog
3 fish
4
5
Świstak skończyłby w wiadrze nr 3.
W tym momencie currentSize to 5.
A teraz średnia liczba elementów na wiadro
to currentSize podzielony przez buckets.length.
To 5 elementów podzielonych przez 6 wiader to 0,83.
A to przekracza współczynnik obciążenia, który wynosił 0,75.
Aby rozwiązać ten problem, aby
wiadra może trochę
bardziej puste, aby operacje takie jak ustalenie, czy a
wiadro zawiera
element będzie trochę mniej skomplikowany, chcę zmienić rozmiar
mój zestaw Hash.
Zmiana rozmiaru zestawu HashSet wymaga dwóch kroków.
Najpierw podwoję liczbę wiader, miałem 6 wiader,
teraz mam 12 wiader.
Zauważ, że loadFactor, który ustawiłem na 0,75, pozostaje taki sam.
Ale liczba zmienionych wiader wynosi 12,
liczba elementów pozostała taka sama, wynosi 5.
5 podzielone przez 12 to około 0,42, czyli znacznie poniżej naszego
Współczynnik obciążenia,
więc teraz mamy się dobrze.
Ale nie skończyliśmy, ponieważ niektóre z tych elementów są w
niewłaściwe wiadro.
Na przykład słoń.
Słoń był w wiadrze numer 2, ponieważ liczba
postacie w słoniu
miał 8 lat
Mamy 6 wiader, 8 minus 6 to 2.
Właśnie dlatego znalazł się na drugim miejscu.
Ale teraz, gdy mamy 12 wiader, 8 mod 12 to 8, więc
słoń nie należy już do wiadra nr 2.
Słoń należy do wiadra nr 8.
A co z świstakiem?
Woodchuck był tym, który zaczął ten cały problem.
Świstak trafił do wiadra nr 3.
Ponieważ 9 mod 6 to 3.
Ale teraz robimy 9 mod 12.
9 mod 12 to 9, świstak trafia do wiadra nr 9.
I widzisz zaletę tego wszystkiego.
Teraz wiadro numer 3 ma tylko dwa elementy, podczas gdy wcześniej miało 3.
Oto nasz kod,
gdzie mieliśmy nasz zestaw HashSet z oddzielnym łańcuchem tym
nie zmieniłem rozmiaru.
Oto nowa implementacja, w której używamy zmiany rozmiaru.
Większość tego kodu jest taka sama,
nadal będziemy ustalać, czy zawiera
wartość już.
Jeśli nie, to ustalimy, które to wiadro
powinien wejść do i
następnie dodaj go do tego segmentu, dodaj do LinkedList.
Ale teraz zwiększamy pole currentSize.
currentSize było polem, które śledziło liczbę
elementów w naszym zestawie Hash.
Zwiększymy to, a potem spojrzymy
przy średnim obciążeniu
średnia liczba elementów na wiadro.
Zrobimy ten podział tutaj.
Żeby się upewnić, musimy trochę rzucić
że otrzymamy podwójne.
Następnie porównamy to średnie obciążenie z polem
które ustawiłem jako
Na przykład 0,75, kiedy utworzyłem ten zestaw HashSet
loadFactor.
Jeśli średnie obciążenie jest większe niż loadFactor,
oznacza to, że na wiadrze jest za dużo elementów
średnia i muszę ponownie wstawić.
Oto nasza implementacja metody ponownego wstawiania
wszystkie elementy.
Najpierw utworzę lokalną zmienną o nazwie oldBuckets.
Co odnosi się do wiader w ich obecnym stanie
zanim zacznę zmieniać rozmiar wszystkiego.
Uwaga: Nie tworzę jeszcze nowej tablicy połączonych list.
Po prostu zmieniam nazwę wiader na oldbuckets.
Pamiętajcie, że wiadra były polem w naszej klasie, idę
aby teraz utworzyć nową tablicę
połączonych list, ale będzie zawierało dwa razy więcej elementów
tak jak za pierwszym razem.
Teraz muszę ponownie wprowadzić,
Powtórzę wszystkie stare wiadra.
Każdy element w oldBuckets jest LinkedList ciągów
to jest wiadro.
Przejdę przez to wiadro i wezmę w to każdy element
wiadro.
A teraz włożę go ponownie do nowych koszyków.
Otrzymam jego hashCode.
Dowiem się, jaki to indeks.
A teraz dostaję nowy segment, nowy LinkedList z
ciągi i
Dodam to do tego nowego wiadra.
Podsumowując, HashSets, jak widzieliśmy, są tablicami Linked
Listy lub wiadra.
HashSet samozmieniający się może zrealizować używając pewnego współczynnika lub
capacity = N/0.75
aby uniknąć powtórzenia, ale moja początkowa myśl właśnie się rozpoczęłaload factor = 1
. Czy takie podejście miałoby wady? Dlaczego by załadować czynnik wpływaget()
iput()
koszty eksploatacji?