Se os baldes ficarem muito cheios, temos que olhar através
uma lista vinculada muito longa.
E isso é meio que derrotar o ponto.
Então, aqui está um exemplo em que tenho quatro baldes.
Eu tenho elefante e texugo no meu HashSet até agora.
Esta é uma situação muito boa, certo?
Cada elemento possui zero ou um elemento.
Agora, colocamos mais dois elementos em nosso HashSet.
buckets elements
------- -------
0 elephant
1 otter
2 badger
3 cat
Isso também não é tão ruim.
Todo depósito possui apenas um elemento. Então, se eu quero saber, isso contém panda?
Eu posso olhar muito rapidamente para o balde número 1 e não é
lá e
Eu sabia que não está em nossa coleção.
Se eu quero saber se ele contém gato, eu olho no balde
número 3,
Encontro gato, sei muito rapidamente se está no nosso
coleção.
E se eu adicionar coala, bem, isso não é tão ruim.
buckets elements
------- -------
0 elephant
1 otter -> koala
2 badger
3 cat
Talvez agora, em vez de no balde número 1, apenas olhando
um elemento
Eu preciso olhar para duas.
Mas pelo menos eu não tenho que olhar para elefante, texugo e
gato.
Se eu estiver novamente procurando por panda, ele só pode estar no balde
número 1 e
Eu não tenho que olhar para outra coisa senão lontra e
coala.
Mas agora eu coloquei jacaré no balde número 1 e você pode
veja talvez para onde isso está indo.
Que se o balde número 1 estiver ficando cada vez maior e maior
maior, então eu basicamente tenho que olhar através de todos
esses elementos para encontrar
algo que deve estar no balde número 1.
buckets elements
------- -------
0 elephant
1 otter -> koala ->alligator
2 badger
3 cat
Se eu começar a adicionar strings a outros buckets,
certo, o problema se torna cada vez maior a cada
balde único.
Como impedimos que nossos baldes fiquem cheios demais?
A solução aqui é que
"the HashSet can automatically
resize the number of buckets."
Há o HashSet percebe que os baldes estão recebendo
muito cheio.
Está perdendo essa vantagem dessa busca única por
elementos.
E apenas criará mais buckets (geralmente duas vezes mais do que antes) e
depois coloque os elementos no balde correto.
Então, aqui está nossa implementação básica do HashSet com
encadeamento. Agora vou criar um "HashSet com redimensionamento automático".
Este HashSet vai perceber que os baldes estão
ficando muito cheio e
precisa de mais baldes.
loadFactor é outro campo em nossa classe HashSet.
loadFactor representa o número médio de elementos por
balde,
acima do qual queremos redimensionar.
loadFactor é um equilíbrio entre espaço e tempo.
Se os baldes ficarem muito cheios, redimensionaremos.
Isso leva tempo, é claro, mas
pode economizar tempo na estrada se os baldes forem um
um pouco mais vazio.
Vamos ver um exemplo.
Aqui está um HashSet, adicionamos quatro elementos até agora.
Elefante, cachorro, gato e peixe.
buckets elements
------- -------
0
1 elephant
2 cat ->dog
3 fish
4
5
Neste ponto, eu decidi que o loadFactor, o
limite,
o número médio de elementos por bloco que eu estou bem
com, é 0,75.
O número de buckets é buckets.length, que é 6 e
Nesse ponto, nosso HashSet possui quatro elementos, portanto, o
o tamanho atual é 4.
Redimensionaremos nosso HashSet, ou seja, adicionaremos mais buckets,
quando o número médio de elementos por bloco exceder
o loadFactor.
É quando o tamanho atual dividido por buckets.length é
maior que loadFactor.
Nesse ponto, o número médio de elementos por bucket
é 4 dividido por 6.
4 elementos, 6 baldes, ou seja, 0,67.
Isso é menor que o limite que eu defini de 0,75, então estamos
OK.
Não precisamos redimensionar.
Mas agora vamos dizer que adicionamos marmota.
buckets elements
------- -------
0
1 elephant
2 woodchuck-> cat ->dog
3 fish
4
5
A marmota acabaria no balde número 3.
Neste ponto, o currentSize é 5.
E agora o número médio de elementos por bucket
é o currentSize dividido por buckets.length.
São 5 elementos divididos por 6 depósitos: 0,83.
E isso excede o loadFactor que foi de 0,75.
Para resolver este problema, a fim de tornar o
baldes talvez um pouco
mais vazio, de modo que operações como determinar se um
balde contém
um elemento será um pouco menos complexo, eu quero redimensionar
meu HashSet.
O redimensionamento do HashSet leva duas etapas.
Primeiro vou dobrar o número de baldes, eu tinha 6 baldes,
agora eu vou ter 12 baldes.
Observe aqui que o loadFactor que defini como 0,75 permanece o mesmo.
Mas o número de buckets alterados é 12,
o número de elementos permaneceu o mesmo, é 5.
5 dividido por 12 é de cerca de 0,42, isso está bem sob nossa
loadFactor,
então estamos bem agora.
Mas não terminamos porque alguns desses elementos estão em
o balde errado agora.
Por exemplo, elefante.
Elefante estava no balde número 2 porque o número de
personagens em elefante
tinha 8 anos
Temos 6 baldes, 8 menos 6 é 2.
Por isso, acabou no número 2.
Mas agora que temos 12 baldes, 8 mod 12 é 8, então
o elefante não pertence mais ao balde número 2.
O elefante pertence ao balde número 8.
E a marmota?
Foi a marmota que iniciou todo esse problema.
A marmota acabou no balde número 3.
Porque 9 mod 6 é 3.
Mas agora fazemos 9 mod 12.
9 mod 12 é 9, a marmota vai para o balde número 9.
E você vê a vantagem de tudo isso.
Agora, o balde número 3 tem apenas dois elementos, enquanto antes tinha 3.
Então aqui está o nosso código,
onde tivemos nosso HashSet com encadeamento separado
não redimensionou.
Agora, aqui está uma nova implementação em que usamos o redimensionamento.
A maior parte desse código é a mesma,
ainda vamos determinar se ele contém o
valor já.
Se não, então vamos descobrir qual balde
deve entrar e
em seguida, adicione-o a esse bucket, adicione-o a esse LinkedList.
Mas agora incrementamos o campo currentSize.
currentSize era o campo que acompanhava o número
de elementos em nosso HashSet.
Vamos incrementá-lo e depois vamos olhar
na carga média,
o número médio de elementos por bloco.
Nós vamos fazer essa divisão aqui em baixo.
Temos que fazer um pouco de casting aqui para garantir
que temos um duplo.
E então, compararemos essa carga média com o campo
que eu coloquei como
0,75 quando criei este HashSet, por exemplo, que era
o loadFactor.
Se a carga média for maior que o loadFactor,
isso significa que há muitos elementos por balde no
média e preciso reinserir.
Então aqui está a nossa implementação do método para reinserir
todos os elementos
Primeiro, vou criar uma variável local chamada oldBuckets.
Que se refere aos baldes como estão atualmente
antes de começar a redimensionar tudo.
Nota: ainda não estou criando uma nova matriz de listas vinculadas.
Estou apenas renomeando baldes como oldBuckets.
Agora lembre-se de baldes era um campo em nossa classe, eu vou
agora criar uma nova matriz
de listas vinculadas, mas isso terá o dobro de elementos
como fez a primeira vez.
Agora eu preciso fazer a reinserção,
Vou percorrer todos os baldes antigos.
Cada elemento em oldBuckets é um LinkedList de strings
isso é um balde.
Vou passar por esse balde e obter cada elemento nesse
balde.
E agora vou reinseri-lo nos newBuckets.
Vou receber o seu hashCode.
Vou descobrir qual é o índice.
E agora eu recebo o novo balde, o novo LinkedList de
cordas e
Vou adicioná-lo a esse novo balde.
Então, para recapitular, o HashSets como vimos são matrizes de Linked
Listas ou baldes.
Um HashSet com redimensionamento automático pode ser realizado usando alguma taxa ou
capacity = N/0.75
para evitar repetições, mas meu pensamento inicial foi definidoload factor = 1
. Haveria desvantagens nessa abordagem? Por que o fator de carga afetariaget()
eput()
os custos operacionais?