Se i secchi diventano troppo pieni, allora dobbiamo guardare attraverso
un lungo elenco di link.
E questo è un po 'come sconfiggere il punto.
Quindi, ecco un esempio in cui ho quattro secchi.
Finora ho un elefante e un tasso nel mio HashSet.
Questa è una situazione abbastanza buona, vero?
Ogni elemento ha zero o un elemento.
Ora inseriamo altri due elementi nel nostro HashSet.
buckets elements
------- -------
0 elephant
1 otter
2 badger
3 cat
Neanche questo è male.
Ogni secchio ha solo un elemento. Quindi, se voglio saperlo, contiene panda?
Posso guardare molto velocemente il secchio numero 1 e non lo è
lì e
Sapevo che non era nella nostra collezione.
Se voglio sapere se contiene un gatto, guardo il secchio
numero 3,
Trovo il gatto, so molto rapidamente se è nel nostro
collezione.
E se aggiungessi il koala, beh, non è poi così male.
buckets elements
------- -------
0 elephant
1 otter -> koala
2 badger
3 cat
Forse ora invece che nel secchio numero 1 solo guardando
un elemento,
Ho bisogno di guardare due.
Ma almeno non devo guardare elefante, tasso e
gatto.
Se sto di nuovo cercando un panda, può essere solo nel secchio
numero 1 e
Non devo guardare altro che lontra e
koala.
Ma ora ho messo il coccodrillo nel secchio numero 1 e puoi farlo
vedi forse dove sta andando.
Che se il secchio numero 1 continua a diventare sempre più grande e
più grande, quindi devo praticamente esaminare tutto
quegli elementi da trovare
qualcosa che dovrebbe essere nel secchio numero 1.
buckets elements
------- -------
0 elephant
1 otter -> koala ->alligator
2 badger
3 cat
Se inizio ad aggiungere stringhe ad altri bucket,
giusto, il problema diventa sempre più grande in ogni
secchio singolo.
Come possiamo evitare che i nostri secchi si riempiano troppo?
La soluzione qui è quella
"the HashSet can automatically
resize the number of buckets."
C'è che HashSet si rende conto che stanno ottenendo i secchi
troppo pieno.
Sta perdendo questo vantaggio di tutta questa ricerca
elementi.
E creerà solo più secchi (generalmente due volte come prima) e
quindi posizionare gli elementi nel secchio corretto.
Quindi ecco la nostra implementazione di base di HashSet con separata
concatenamento. Ora creerò un "HashSet auto-ridimensionante".
Questo HashSet si renderà conto che i secchi lo sono
diventando troppo pieno e
ha bisogno di più secchi.
loadFactor è un altro campo nella nostra classe HashSet.
loadFactor rappresenta il numero medio di elementi per
secchio,
sopra il quale vogliamo ridimensionare.
loadFactor è un equilibrio tra spazio e tempo.
Se i secchi si riempiono troppo, ridimensioneremo.
Ci vuole tempo, ovviamente, ma
potrebbe farci risparmiare tempo lungo la strada se i secchi sono a
un po 'più vuoto.
Vediamo un esempio.
Ecco un HashSet, finora abbiamo aggiunto quattro elementi.
Elefante, cane, gatto e pesce.
buckets elements
------- -------
0
1 elephant
2 cat ->dog
3 fish
4
5
A questo punto, ho deciso che loadFactor, il
soglia,
il numero medio di elementi per secchio che sto bene
con, è 0,75.
Il numero di bucket è buckets.length, che è 6 e
a questo punto il nostro HashSet ha quattro elementi, quindi il
la dimensione attuale è 4.
Ridimensioneremo il nostro HashSet, ovvero aggiungeremo altri bucket,
quando il numero medio di elementi per bucket supera
il loadFactor.
Cioè quando la dimensione attuale divisa per bucket.length è
maggiore di loadFactor.
A questo punto, il numero medio di elementi per bucket
è 4 diviso per 6.
4 elementi, 6 secchi, questo è 0.67.
Questo è inferiore alla soglia che ho impostato di 0,75, quindi lo siamo
va bene.
Non abbiamo bisogno di ridimensionare.
Ma ora diciamo che aggiungiamo marmotta.
buckets elements
------- -------
0
1 elephant
2 woodchuck-> cat ->dog
3 fish
4
5
Woodchuck sarebbe finito nel secchio numero 3.
A questo punto, currentSize è 5.
E ora il numero medio di elementi per bucket
è la dimensione corrente divisa per buckets.length.
Quello è 5 elementi divisi per 6 secchi è 0,83.
E questo supera il loadFactor che era 0,75.
Al fine di affrontare questo problema, al fine di rendere il
secchi forse un po '
più vuoto in modo che operazioni come determinare se a
contiene secchio
un elemento sarà un po 'meno complesso, voglio ridimensionare
il mio HashSet.
Il ridimensionamento di HashSet richiede due passaggi.
Per prima cosa raddoppierò il numero di secchi, ne avevo 6,
ora avrò 12 secchi.
Nota qui che il loadFactor che ho impostato su 0,75 rimane lo stesso.
Ma il numero di bucket modificati è 12,
il numero di elementi è rimasto lo stesso, è 5.
5 diviso 12 è circa 0,42, che è ben al di sotto della nostra
fattore di carico,
quindi ora stiamo bene.
Ma non abbiamo finito perché alcuni di questi elementi sono presenti
il secchio sbagliato ora.
Ad esempio, elefante.
Elephant era nel secchio numero 2 perché il numero di
personaggi in elefante
aveva 8 anni.
Abbiamo 6 secchi, 8 meno 6 è 2.
Ecco perché è finito nel numero 2.
Ma ora che abbiamo 12 secchi, 8 mod 12 è 8, quindi
l'elefante non appartiene più al secchio numero 2.
L'elefante appartiene al secchio numero 8.
Che dire di marmotta?
Woodchuck è stato colui che ha iniziato l'intero problema.
Woodchuck è finito nel secchio numero 3.
Perché 9 mod 6 è 3.
Ma ora facciamo 9 mod 12.
9 mod 12 è 9, woodchuck va al secchio numero 9.
E vedi il vantaggio di tutto questo.
Ora il secchio numero 3 ha solo due elementi mentre prima ne aveva 3.
Quindi ecco il nostro codice,
dove abbiamo avuto il nostro HashSet con questo incatenamento separato
non ha fatto alcun ridimensionamento.
Ora, ecco una nuova implementazione in cui utilizziamo il ridimensionamento.
Gran parte di questo codice è lo stesso,
determineremo ancora se contiene il file
valore già.
In caso contrario, scopriremo quale secchio
dovrebbe andare in e
quindi aggiungilo a quel bucket, aggiungilo a quell'elenco collegato.
Ma ora incrementiamo il campo currentSize.
currentSize era il campo che teneva traccia del numero
di elementi nel nostro HashSet.
Lo incrementeremo e poi guarderemo
al carico medio,
il numero medio di elementi per bucket.
Faremo quella divisione qui.
Dobbiamo fare un po 'di casting qui per essere sicuri
che otteniamo un doppio.
E poi, confronteremo quel carico medio con il campo
che ho impostato come
0.75 quando ho creato questo HashSet, ad esempio, che era
il loadFactor.
Se il carico medio è maggiore di loadFactor,
ciò significa che ci sono troppi elementi per secchio
nella media e devo reinserirlo.
Quindi, ecco la nostra implementazione del metodo per reinserirla
tutti gli elementi.
Innanzitutto, creerò una variabile locale chiamata oldBuckets.
Che si riferisce ai secchi così come sono attualmente
prima di iniziare a ridimensionare tutto.
Nota Non sto ancora creando un nuovo array di elenchi collegati.
Sto solo rinominando i bucket come oldBuckets.
Ora ricordo che i secchi erano un campo nella nostra classe, vado
per ora creare un nuovo array
di elenchi collegati ma questo avrà il doppio degli elementi
come ha fatto la prima volta.
Ora devo effettivamente reinserire,
Esaminerò tutti i vecchi secchi.
Ogni elemento in oldBuckets è un LinkedList di stringhe
quello è un secchio.
Esaminerò quel secchio e ne prenderò ogni elemento
secchio.
E ora lo reinserirò nei newBuckets.
Prenderò il suo hashCode.
Capirò quale indice è.
E ora ho il nuovo bucket, il nuovo LinkedList di
stringhe e
Lo aggiungerò a quel nuovo secchio.
Quindi, per ricapitolare, gli Hashset come abbiamo visto sono array di Linked
Elenchi o secchi.
Un HashSet auto-ridimensionante può realizzare usando un certo rapporto o
capacity = N/0.75
di evitare il reinserimento, ma il mio pensiero iniziale era appena stato impostatoload factor = 1
. Ci sarebbero svantaggi di questo approccio? Perché il fattore di carico influirebbeget()
e iput()
costi operativi?