Busca em HashMaps[Resolvido]

3 respostas
D

Olá pessoal. Eu estou estudando para a certificação pelo livro da Kathy Sierra. Estou no capítulo sobre Collections e Genéricos. Cheguei na parte em que é explicado o funcionamento da interface Map, mas não consegui entender direito o seu funcionamento. Me deu um nó na cabeça. Bom, vou postar o pedaço do código da minha dúvida e tentar explicar o que eu não entendi.
Primeiramente há uma classe Dog que implementa de forma correta os métodos equals() e hashCode():

import java.util.*;

class Dog{

   public Dog(String n) { name = n; }
   public String name;

   public boolean equals(Object o){

      if((o instanceof Dog) && ((Dog)o).name == name){
          return true;
      }  else{
           return false;
      }

   }

   public int hashCode() { return name.length(); }

}

Depois a classe que usa o Map:

class MapTest{

   public static void main(String[] args){
      Map<Object, Object> m = new HashMap<Object, Object>();

      Dog d1 = new Dog("clover");
      m.put(d1, "Dog Key");

      System.out.println(m.get(d1));
     
      d1.name = "magnolia";
     System.out.println(m.get(d1));

     d1.name = "clover";
     System.out.println(m.get(new Dog("clover")));

     d1.name="arthur";
     System.out.println(m.get(new Dog("clover")));    
  
   }
}

A saída desse código é: Dog Key, null, Dog Key, null.
Ok, vamos lá. A primeira saída é fácil de entender. Afinal, foi criada uma chave que é um objeto Dog para o valor String “Dog Key”. Minha dúvida começa a partir do segundo get. O código muda o nome do Dog de “clover” para “magnolia” e usa o próprio objeto para buscar o valor “Dog Key”. Mesmo que o nome tenha mudado, e consequentemente o código hash da chave também, se o próprio objeto é a chave, por que ele não conseguiu achar “Dog Key”? Pois a chave é o objeto referenciado por d1 e o método get é chamado usando-se o d1. Com isso eu imaginei: "Bom, ele deve ter mantido o código hashing de quando o valor foi inserido junto com a chave na primeira chamada a put() e essa chave é 6, diferente de ‘magnolia’ que é 8’ ". Mas se fosse dessa maneira que pensei, para mim o último get deveria retornar o valor correto “Dog Key” e não null. Pois como estou passando um objeto com o nome “clover” e na hora da inserção chave/valor, o objeto era “clover”, ele deveria retornar o valor correto. Mas não é desse jeito. Ele passou um objeto Dog novo e o comparou com o objeto dog que é a chave, o qual é referenciado por d1. Como possuem valores diferentes para nome, não passam no teste do equals(). Mas por que no caso de “magnolia” não deu certo se a própria chave é o d1? Não sei se fui muito compreensivo na dúvida, mas…
Agradeço a ajuda e desculpem pelo longo texto.

Abraços!

3 Respostas

P
douglas_vidotto:
if((o instanceof Dog) && ((Dog)o).name == name){
A comparação de Strings não deve ser feita com ==
T

A observação do pmlm está correta, mas acho que não esclarece sua dúvida. Supondo que você implemente o equals corretamente assim:

Não sei se entendi direito a sua dúvida, mas o que ocorre é o seguinte.

Dog d1 = new  Dog("clover");  
m.put(d1, "Dog Key");

Aqui o hash de d1 é calculado. Como d1 é na verdade um Dog, o Java vai usar a implementação hash e equals da sua classe Dog. Ao dar o put acima, o hash é calculado. Pela implementação da sua classe Dog o hash acaba usando simplesmente a implementação hash da classe String. Assim, o hash do atributo “name” é calculado. Vamos supor (só para ficar mais fácil) que esse hash tem 10 “recipientes” e que, ao calcular esse primeiro hash (“clover”) tal hash dê 2. Logo, d1 será colocado no “recipiente” 2.

d1.name = "magnolia"; System.out.println(m.get(d1));

Aqui vc mudou o atributo do objeto d1 para “magnolia”, porém, d1 continua no “recipiente” 2 !!! Ao fazer m.get(d1), o hash é calculado, mas dessa vez, é calculado com relação à String “magnolia”. Vamos supor que tal hash dê 6. Assim, o Java vai sair percorrendo o “recipiente” 6 e comparando um a um com o método equals até ver se acha algum objeto igual (esse igual é sempre com relação ao método equals) ao passado no parêmetro get. Porém, não há nenhum objeto no “recipiente” 6! (Apenas no 2). Logo, ele não acha nada e retorna null.

d1.name = "clover";  
System.out.println(m.get(new Dog("clover")));

Aqui vc mudou o parâmetro novamente para “clover”. O objeto d1 continua no “recipiente” 2. Na chamada do get, ele vai usar o hashcode novamente, mas dessa vez o hashcode vai dar 2 também (pois é a mesma String e o hashcode implementado por você apenas usa essa String). Assim, o Java vai sair comparando tudo que estiver no “recipiente” 2 com o método equals. Agora sim, como d1 está ali no recipiente 2 (e d1.name possui a String “clover”), ele vai encontrá-lo e, logo, retornar o valor correspondente (no caso, “Dog key”).

d1.name="arthur";  
System.out.println(m.get(new Dog("clover")));

Aqui vai acontecer a mesma coisa do passo anterior. Porém, na hora do Java comparar com o método equals no “recipiente” 2, ele não vai encontrar nada (pois o atrivuto “name” de d1 não é mais “clover” e sim “arthur”). Logo a comparação com equals falha e retorna null (não achou nada).

Acho que era essa a sua dúvida. Se não for, poste novamente que eu tento ajuda de outra forma. :smiley:

D

Fala Tiago…blz? Minha dúvida era exatamente essa e você conseguiu esclarecer muito bem!! Muito obrigado. Quanto ao código em que ele faz a comparação de Strings com ==, realmente está inadequado. Mas eu copiei exatamente como estava no livro (tirando só os pedaços que não faziam parte da dúvida). Mas confesso que nem tinha prestado atenção nisso, hehehehe.

Criado 16 de fevereiro de 2010
Ultima resposta 16 de fev. de 2010
Respostas 3
Participantes 3