Comparação de várias maneiras de julgar os mesmos elementos de Hashmap, Hashset, Treemap e Treeset em Java
1.1 HashMap
Vamos primeiro dar uma olhada em como os elementos são armazenados no hashmap. Cada elemento armazenado no mapa é um par de valores-chave como o valor-chave e é adicionado através do método PUT. Além disso, a mesma chave terá apenas um valor associado a ela no mapa. O método put é definido no mapa da seguinte forma.
V put (K -Key, V Valor);
É usado para armazenar um par de valores-chave como o valor-chave. O valor de retorno é o valor antigo armazenado no mapa pela chave. Se não existir antes, retornará nulo. É assim que o método de put hashmap é implementado.
public V put (K -Key, V valor) {if (key == null) retorna putformullKey (valor); int hash = hash (chave); int i = indexfor (hash, tabela.length); para (entrada <k, v> e = tabela [i]; e! = null; e = e.next) {objeto k; if (e.hash == hash && ((k = e.Key) == key || key.equals (k))) {v OldValue = e.value; E.Value = Value; E.RecordAccess (isto); retornar OldValue; }} modCount ++; addentry (hash, chave, valor, i); retornar nulo; }Do exposto, podemos ver que, ao adicionar a combinação correspondente de valor-chave, se a chave correspondente já existir, o valor correspondente será alterado diretamente e o valor antigo será retornado. Ao julgar se a chave existe, o código de hash da chave é comparado primeiro e, em seguida, o código de hash da chave é comparado com iguais ou iguais. Podemos não ser capazes de vê -lo aqui. A partir do código acima, comparamos o hashcode do map.entry e o código de hash do Key. De fato, podemos ver que o mapa. Se a tecla correspondente não existir originalmente, o addentry será chamado para adicionar o valor da chave correspondente ao mapa. O hash de parâmetro aprovado por addentry é o código de hash correspondente à chave. Em seguida, vamos dar uma olhada na definição do método de addentry.
Void Addentry (int hash, K Key, V Valor, int BucketIndex) {if ((size> = limiar) && (null! = tabela [bucketIndex])) {redimensionamento (2 * tabela.length); hash = (null! = chave)? hash (chave): 0; bucketIndex = indexfor (hash, tabela.length); } createEntry (hash, chave, valor, bucketIndex); } void createEntry (int hash, k key, v valor, int bucketIndex) {entrada <k, v> e = tabela [bucketIndex]; tabela [bucketIndex] = nova entrada <> (hash, chave, valor, e); tamanho ++; }O núcleo do addEntry é chamar CreateEntry para criar um objeto Map.Entry representando o valor da chave correspondente e armazená-lo. Obviamente, a tabela acima é uma matriz de mapa.entry. Map.Entry usa um hash de propriedade para salvar o código de hash da tecla correspondente. Vamos dar uma olhada no construtor do mapa.entry chamado acima.
Entrada (int h, k k, v v, entrada <k, v> n) {value = v; próximo = n; chave = k; hash = h; }Obviamente, ele salva o código de hash correspondente à chave, valor e chave correspondentes.
Depois de entender como o Hashmap armazena elementos, é mais fácil ver como o Hashmap armazena elementos. Existem dois métodos principais no hashmap para determinar se os elementos são iguais. Um é determinar se a chave é a mesma e a outra é determinar se o valor é o mesmo. De fato, ao introduzir como o Hashmap armazena elementos, introduzimos como o hashmap determina se a chave do elemento é a mesma. Ou seja, antes de tudo, o código de hash é o mesmo e, em segundo lugar, as teclas são iguais ou iguais. A determinação de se a chave é a mesma no mapa é realizada através do método containsKey () e sua implementação no hashmap é a seguinte.
public boolean containsKey (chave do objeto) {return getentry (key)! = null; } Entrada final <k, v> getentry (chave do objeto) {int hash = (key == null)? 0: Hash (chave); for (entrada <k, v> e = tabela [indexfor (hash, tabela.length)]; e! = null; e = e.next) {objeto k; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k)))) retornar e; } retornar nulo; }É óbvio que primeiro determina se o código de hash da chave é o mesmo e, em seguida, determina se a chave é igual ou é igual.
O valor usado no mapa é julgado pelo método Containsvalue, e sua implementação no HashMap é a seguinte.
public boolean containsValue (valor do objeto) {if (value == null) retorna contémnullValue (); Entrada [] guia = tabela; for (int i = 0; i <tab.length; i ++) para (entrada e = tab [i]; e! = null; e = e.next) if (value.equals (e.value)) retorna true; retornar falso; }Obviamente, o valor da forma não nula é julgado pelos iguais do valor, e a forma nula é o tempo que for igual, ou seja, o valor no elemento salvo é nulo.
1.2 HashSet
Depois de saber como o Hashmap armazena elementos e determina se os elementos são iguais, é mais fácil ver como o hashset determina se os elementos são iguais.
Os elementos no hashset são realmente salvos através do hashmap. Cada objeto de hashset mantém uma referência ao objeto hashmap correspondente. Ao adicionar e excluir elementos ao hashset, ele é realizado através do hashmap que ele possui. Ao salvar um elemento, ele salvará o elemento correspondente como a chave do hashmap mantido. O valor correspondente é um objeto constante; portanto, ao salvá -lo, ele usa a lógica de determinar se os elementos são iguais. Ao determinar se um elemento está incluído, ele também chama o método Containskey do hashmap mantido para fazer julgamentos.
public boolean contém (objeto o) {returnmap.containskey (o); }Amigos interessados podem conferir o código -fonte do hashset.
1.3 Treemap
Os elementos armazenados no TREEMAP são ordenados e classificados de acordo com a chave. Treemap tem duas maneiras de classificar elementos armazenados. Um é classificar através do comparador que ele mantém, e o outro é classificar a chave que implementa a interface comparável. O primeiro método é preferido. Quando o comparador mantido (o padrão é nulo) é nulo, o segundo método é usado. O TREEMAP possui vários construtores, através dos quais o comparador que ele mantém pode ser inicializado. Vamos primeiro olhar como o Treemap salva elementos. O método put é implementado da seguinte maneira.
public v put (K -Key, V Valor) {Entrada <K, V> T = ROOT; if (t == null) {compare (chave, chave); // tipo (e possível nulo) Verifique root = nova entrada <> (chave, valor, nulo); tamanho = 1; modCount ++; retornar nulo; } int cmp; Entrada <k, v> pai; // Comparador dividido e comparador de caminhos comparáveis <??? Super K> CPR = Comparador; if (CPR! = null) {do {parent = t; cmp = cpr.comPare (chave, t.key); if (cmp <0) t = t.left; elseif (cmp> 0) t = t.right; caso contrário, retorne t.setValue (valor); } while (t! = null); } else {if (key == null) thrownew nullPointerException (); Comparável <? Super K> K = (Comparável <? Super K>) Tecla; do {parent = t; cmp = k.compareto (t.key); if (cmp <0) t = t.left; elseif (cmp> 0) t = t.right; caso contrário, retorne t.setValue (valor); } while (t! = null); } Entrada <k, v> e = nova entrada <> (chave, valor, pai); if (cmp <0) parent.left = e; else parent.right = e; FixAfterInserção (e); tamanho ++; modCount ++; retornar nulo; }A partir da implementação acima, podemos ver que o primeiro elemento será armazenado diretamente. Os seguintes elementos são divididos em duas situações, um é o caso em que o comparador mantido não está vazio e o outro é o caso em que o comparador mantido está vazio. Quando o comparador não estiver vazio, o comparador determinará a localização do elemento armazenado. Uma coisa importante é que, quando o resultado de comparar a chave do elemento existente com a chave do elemento armazenado atual é 0, será considerado que a chave do elemento armazenado atualmente já existe no mapa original e, em seguida, o valor correspondente à chave original é alterado para o novo valor e, em seguida, o valor antigo é retornado diretamente. Quando o comparador mantido estiver vazio, a localização do elemento será determinada pelo método compareto da chave que implementa a interface comparável. Uma coisa semelhante ao uso do comparador é que, quando a chave original é comparada com a chave recém-armazenada é 0, será considerado que a chave recém-armazenada já existe no mapa original e o valor da chave original correspondente será alterado diretamente e o par de valores-chave não será mais armazenado. De fato, a principal lógica de implementação do método ContainsKey que determina se o elemento existe é semelhante e a implementação específica é a seguinte.
public boolean containsKey (chave do objeto) {return getentry (key)! = null; } Entrada final <k, v> getentry (chave do objeto) {// Versão baseada em comparador de descarte para uma questão de desempenho se (comparador! = null) retorna getentryUsingComParator (chave); if (key == null) tocou nullPointerException (); Comparável <? Super K> K = (Comparável <? Super K>) Tecla; Entrada <k, v> p = root; while (p! = null) {int cmp = k.compareto (p.Key); if (cmp <0) p = p.left; elseif (cmp> 0) p = p.right; caso contrário, P; } retornar nulo; }Como a lógica do TREEMAP para determinar se existe um elemento é determinar se o resultado após a comparação do comparador ou comparável é 0, devemos ter cuidado ao usar o TREEMAP para implementar alguma lógica semelhante ao elemento igual.
A lógica do TREEMAP contémValue é determinar se o valor correspondente é igual? Semelhante ao hashmap. Amigos interessados podem verificar o código -fonte do Treemap.
1.4 Treeset
TreeSet também é uma implementação do conjunto. Os elementos armazenados não são repetidos e são ordenados. Por padrão, os elementos armazenados devem implementar a interface comparável, porque, por padrão, os elementos serão comparados como objetos comparáveis. O TreeSet também pode ser usado para comparar os elementos armazenados nele através do comparador. Isso pode ser alcançado passando em um objeto comparador ou em um Treemap mantendo um objeto comparador ao construir um árvore. A implementação do TreeSet é semelhante à do hashset. Ele também mantém uma referência a um mapa, mas não se refere a um hashmap, mas a TreeMap. A adição e exclusão de elementos em TreeSet são todos implementados pelo Treemap que eles mantêm. Portanto, semelhante ao hashset, a maneira de determinar se os elementos em Treeset são os mesmos é consistente com o TREEMAP e também é determinado através do comparador ou comparável, em vez do método tradicional igual. Amigos interessados podem verificar o código -fonte do TreeSet.
Obrigado pela leitura, espero que isso possa ajudá -lo. Obrigado pelo seu apoio a este site!