Eu concordo com:
- a complexidade amortizada geral de O (1)
- uma
hashCode()
implementação ruim pode resultar em várias colisões, o que significa que, na pior das hipóteses, todo objeto vai para o mesmo depósito, portanto, O ( N ) se cada depósito for apoiado por a List
.
- desde o Java 8,
HashMap
substitui dinamicamente os nós (lista vinculada) usados em cada bloco pelos TreeNodes (árvore vermelho-preta quando uma lista fica maior que 8 elementos), resultando em um pior desempenho de O ( logN ).
Mas, isso NÃO é verdade, se queremos ser 100% precisos. A implementação hashCode()
e o tipo de chave Object
(imutável / armazenado em cache ou sendo uma coleção) também podem afetar a complexidade real em termos estritos.
Vamos assumir os três casos a seguir:
HashMap<Integer, V>
HashMap<String, V>
HashMap<List<E>, V>
Eles têm a mesma complexidade? Bem, a complexidade amortizada do 1º é, como esperado, O (1). Mas, quanto ao resto, também precisamos calcular hashCode()
o elemento de pesquisa, o que significa que talvez tenhamos que percorrer matrizes e listas em nosso algoritmo.
Vamos supor que o tamanho de todas as matrizes / listas acima seja k . Então, HashMap<String, V>
e HashMap<List<E>, V>
terá O (k) complexidade amortizada e, similarmente, O ( k + logN ) no pior caso em Java8.
* Observe que o uso de uma String
chave é um caso mais complexo, porque é imutável e o Java armazena em cache o resultado de hashCode()
uma variável privada hash
, portanto é computado apenas uma vez.
/** Cache the hash code for the string */
private int hash; // Default to 0
Mas, o acima exposto também está tendo seu pior caso, porque a String.hashCode()
implementação do Java está verificando se hash == 0
antes da computação hashCode
. Mas ei, existem Strings não vazias que produzem um hashcode
zero, como "f5a5a608", veja aqui ; nesse caso, a memorização pode não ser útil.