Pode-se dizer que os métodos hashCode () e Equals () são uma característica importante da Java completamente orientada a objetos. Ele facilita nossa programação e também traz muitos perigos. Neste artigo, discutiremos como entender e usar corretamente esses dois métodos.
Se você decidir reescrever o método Equals (), deverá estar claro sobre os riscos trazidos ao fazê -lo e verifique se você pode escrever um método robusto iguals (). Uma coisa que você deve observar é que, após a reescrita Equals (), você deve reescrever o método hashcode (). Os motivos específicos serão explicados mais tarde.
Vamos primeiro dar uma olhada na descrição do método iguals () na especificação Javase 7:
・ É reflexivo: para qualquer valor de referência não nulo x, x.equals(x) deve retornar true .
・ É simétrico: para quaisquer valores de referência não nulos x e y, x.equals(y) deverá retornar true se e somente se y.equals(x) retornar true .
・ É transitivo: para quaisquer valores de referência não null x, y, e z, se x.equals(y) retornar TRUE e y.equals(z) retorna true , então x.equals(z) deve retornar true.
・ É consistente: para qualquer valores de referência não nulos x e y , múltiplas invocações de x.equals(y) retornam consistentemente true ou retornam consistentemente false , desde que nenhuma informação usada em comparações iguais nos objetos seja modificada.
・ Para qualquer valor de referência não nulo x, x.equals(null) deve retornar false .
Esta passagem usa muita numerologia em matemática discreta. Deixe -me dar uma breve explicação:
1. Reflexividade: A.Equals (a) deve retornar verdadeiro.
2. Simetria: se A.Equals (b) retornar TRUE, B.Equals (a) também precisará retornar verdadeiro.
3. Transmissão: Se A.quals (b) for verdadeiro e B.Equals (c) é verdadeiro, então A.quals (c) também deve ser verdadeiro. Para ser franco, a = b, b = c, depois a = C.
4. Consistência: Enquanto o estado dos objetos A e B não mudar, A.Equals (b) deve sempre retornar verdadeiro.
5. A.quals (nulo) para retornar falso.
Acredito que, desde que as pessoas que não sejam profissionais em matemática não chamarão as coisas acima. Na aplicação real, precisamos apenas reescrever o método iguals () de acordo com certas etapas. Por conveniência de explicação, primeiro definimos uma classe de programador (codificador):
classe codificador {nome da string privada; private Int Age; // getters and setters}O que queremos é que, se os nomes e idades dos dois objetos do programador forem iguais, pensamos que esses dois programadores são iguais. Neste momento, temos que reescrever seu método iguals (). Como o padrão equals () realmente determina se duas referências apontam para o mesmo objeto intrínseco, é equivalente a ==. Ao reescrever, siga as três etapas seguintes:
1. Determine se é igual a si mesmo.
if (outros == this) retorna true;
2. Use a instância do operador para determinar se outro é um objeto de codificador de tipo.
if (! (outra instância do codificador)) retorna false;
3. Compare os domínios de dados, nome e idade que você personaliza na classe Coder, e você não deve perder um.
Codificador o = (codificador) outro; retornar o.name.equals (nome) && o.age == Age;
Vendo isso, alguém pode perguntar, há um elenco na etapa 3. Se alguém passar por um objeto de classe inteira para isso é igual, ele jogará uma classe de classe? Essa preocupação é realmente redundante. Como fizemos o julgamento de instância na segunda etapa, se outro é um objeto não codificador, ou mesmo outro, é nulo, o False será retornado diretamente nesta etapa, para que o código subsequente não tenha a oportunidade de ser executado.
As três etapas acima também são as etapas recomendadas em <Java eficaz>, que podem basicamente garantir que não haja erro.
Na especificação Javase 7,
"Observe que geralmente é necessário substituir o método HashCode sempre que esse método (igual) é substituído, de modo a manter o contrato geral para o método HashCode, que afirma que objetos iguais devem ter códigos de hash iguais".
Se você reescrever o método iguals (), lembre -se de reescrever o método hashcode (). Aprendemos tabelas de hash nos cursos de estrutura de dados da universidade. O método hashcode () serve a tabela de hash.
Quando usamos uma classe de coleção que começa com hash como hash, como hashmap e hashset, o hashcode () será chamado implicitamente para criar um relacionamento de mapeamento de hash. Vamos explicar isso mais tarde. Aqui vamos nos concentrar na redação do método hashcode () primeiro.
<Eficaz java> fornece um método de escrita que pode evitar conflitos de hash na maior extensão, mas pessoalmente acho que não é necessário fazer tantos problemas para aplicações gerais. Se você precisar armazenar dezenas de milhares ou milhões de objetos em seu aplicativo, siga estritamente os métodos fornecidos no livro. Se você estiver escrevendo um aplicativo pequeno e médio, os seguintes princípios serão suficientes:
É necessário garantir que todos os membros do objeto do codificador possam ser refletidos no HashCode.
Para este exemplo, podemos escrever isso:
@Override public int hashCode () {int resultado = 17; resultado = resultado * 31 + name.hashcode (); resultado = resultado * 31 + idade; resultado de retorno; }Onde o INT resultado = 17 você também pode alterá -lo para 20, 50, etc. Vendo isso, eu estava subitamente curioso e queria ver como o método hashcode () na classe String é implementado. Verifique a documentação e saiba:
"Retorna um código de hash para esta string. O código de hash para um objeto String é calculado como
s [0]*31^(n-1) + s [1]*31^(n-2) + ... + s [n-1]
Usando a aritmética int, onde S [i] é o com o comitê do caráter da sequência, n é o comprimento da corda e ^ indica exponenciação. (O valor de hash da string vazia é zero.) "
Calcule o código ASCII de cada caractere à potência n - 1 e adicione -o. Pode -se observar que o Sun é muito rigoroso na implementação do HashCode. Isso pode evitar o mesmo código de hash nas duas seqüências diferentes na maior extensão.
O conceito de balde é referenciado na implementação da tabela de hash da Oracle. Como mostrado na figura abaixo:
Como pode ser visto na figura acima, a tabela de hash com balde é aproximadamente equivalente a uma combinação de uma tabela de hash e uma lista vinculada. Ou seja, uma lista vinculada será pendurada em cada balde e cada nó da lista vinculado será usado para armazenar objetos. Java usa o método hashcode () para determinar qual bucket um objeto deve estar localizado e, em seguida, o pesquisa na lista vinculada correspondente. Idealmente, se o seu método hashcode () for escrito o suficiente, cada balde terá apenas um nó, que atingirá a complexidade do tempo de nível constante da operação de pesquisa. Ou seja, não importa em qual peça de memória seu objeto é colocado, posso localizar imediatamente a área através do hashcode () sem atravessar e pesquisar do começo ao fim. Esta também é a principal aplicação de tabelas de hash.
como:
Quando chamamos o método Put (Object O) do hashset, primeiro o localizaremos no balde correspondente de acordo com o valor de retorno de O.HashCode (). Se não houver nós no balde, coloque o aqui. Se já houver nós, espere o final da lista vinculada. Da mesma forma, quando a chamada contém (objeto O), o Java localizará o bucket correspondente através do valor de retorno do HashCode () e, em seguida, chama o método Equals (), por sua vez, nos nós na lista vinculada correspondente para determinar se o objeto no nó é o objeto que você deseja.
Vamos usar um exemplo para experimentar esse processo:
Vamos criar dois novos objetos de codificador primeiro:
Codificador c1 = novo codificador ("Bruce", 10); Codificador c2 = novo codificador ("Bruce", 10);Suponha que reescrevemos o método Equals () de codificador sem reescrever o método hashcode ():
@Override public boolean é igual (objeto outro) {System.out.println ("Método igual a Método!"); if (outros == this) retorna true; if (! (outra instância do codificador)) retorna false; Codificador o = (codificador) outro; retornar o.name.equals (nome) && o.age == Age; }Em seguida, construímos um hashset e colocamos o objeto C1 no conjunto:
SET <Coder> set = new HashSet <Coder> (); set.add (C1);
Executar novamente:
System.out.println (set.contains (c2));
Esperamos que o método contém (C2) retorne verdadeiro, mas, na verdade, ele retorna falsa.
O nome e a idade de C1 e C2 são os mesmos. Por que eu chamo contém (C2) e retorne FALSE depois de colocar C1 em um hashset? Este é o hashcode () que está causando problemas. Como você não reescreve o método hashcode (), quando o hashset procurar C2, ele o procurará em diferentes baldes. Por exemplo, se C1 for colocado no balde 05, ele será pesquisado no balde 06 ao procurar C2, então é claro que ele não pode ser encontrado. Portanto, o objetivo de nossa reescrita hashCode () é que, quando os A.Equals (b) retorna true, o hashCode () de A e B deve retornar o mesmo valor.
Peço ao hashcode () para retornar uma linha de números fixa sempre
Alguém pode reescrever assim:
@Override public int hashCode () {return 10; }Se for esse o caso, Hashmap, Hashset e outras aulas de coleta perderão seu "significado de hash". Nas palavras de <Java eficaz>, a tabela de hash degenera em uma lista vinculada. Se o hashcode () retornar o mesmo número todas as vezes, todos os objetos serão colocados no mesmo balde e, cada vez que você executar uma operação de pesquisa, ele atravessará a lista vinculada, que perderá completamente a função do hash. Portanto, é melhor fornecer um hashcode robusto () como uma boa ideia.
O exposto acima é toda a introdução detalhada deste artigo sobre reescrever métodos hashcode () e equals (). Espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la. Obrigado amigos pelo seu apoio para este site!