[RESOLVIDO] Utilizando objeto como chave em um Map

Boa noite pessoal,

No livro SCJP 6, no capítulo de genéricos e coleções, página 327, é dada as seguintes classes:


public class Dog {

	public String name;
	
	public Dog(String n) {
		this.name = n;
	}
	
	@Override
	public boolean equals(Object obj) {
		if ((obj instanceof Dog) && ((Dog) obj).name == name) {
			return true;
		} else {
			return false;
		}
	}
	
	@Override
	public int hashCode() {
		return name.length();
	}
}
public class Cat {}

import java.util.HashMap;
import java.util.Map;

public class MapTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Dog d1 = new Dog("clover");
		
		m.put(d1, "Dog key");
		m.put(new Cat(), "Cat key");
		
		System.out.println(m.get(d1));
		System.out.println(m.get(new Cat()));
	}

}

O livro diz que a saída será:
Dog key
null

O que está correto, o que eu não estou entendo é a explicação que o livro dá:
“O ponto importante aqui é que o método get() não conseguiu encontrar o objeto Cat que foi inserido anteriormente…
…Por que foi possível usar uma instancia de Dog como chave, enquanto que usar uma instancia de Cat como chave não teve sucesso?
É fácil ver que Dog substituiu equals() e hashCode(), enquanto que Cat não o fez.”

Pra mim pelo que eu entendi o retorno é null porque são utilizadas duas referencias distintas de Cat, tanto que se alterar o trecho de código de modo a utilizar a mesma referencia de Cat tanto na inserção quanto na busca, o retorno será “Cat key”…

Nesse caso eu não estou entendendo bem ou no livro essa explicação não procede?

Valeu!

Essa explicação procede…

Faça um teste:

Map&lt;Object, Object&gt; m = new HashMap&lt;Object, Object&gt;();  
          
        Dog d1 = new Dog("clover");  
          
        m.put(d1, "Dog key");  

Cat cat1 = new Cat();
        m.put(cat1, "Cat key"); 
Cat cat2 = new Cat();

System.out.println(d1.hashCode());
System.out.println(cat1.hashCode());
System.out.println(cat2.hashCode());

Os 3 números que serão mostrados vão ser diferentes. O map usa esse número para saber de qual registro você está querendo fazer o get…

[quote=RodrigoM91]
O que está correto, o que eu não estou entendo é a explicação que o livro dá:
“O ponto importante aqui é que o método get() não conseguiu encontrar o objeto Cat que foi inserido anteriormente…
…Por que foi possível usar uma instancia de Dog como chave, enquanto que usar uma instancia de Cat como chave não teve sucesso?
É fácil ver que Dog substituiu equals() e hashCode(), enquanto que Cat não o fez.”

Pra mim pelo que eu entendi o retorno é null porque são utilizadas duas referencias distintas de Cat, tanto que se alterar o trecho de código de modo a utilizar a mesma referencia de Cat tanto na inserção quanto na busca, o retorno será “Cat key”…

Nesse caso eu não estou entendendo bem ou no livro essa explicação não procede?
Valeu![/quote]
A explicação está correta. O que foi colocado é que se você cria dois objetos cats o hascode e equals serão baseados na referencia desses objetos na memória pois não foi sobrescrito hascode e equals, então ele não encontrará a resposta. Agora se você fizer:

System.out.println(m.get(new Dog(“clover”)));

será retornado “Dog key” pois como a classe sobrescreve hashcode e equals as duas classes são iguais se o nome do cachorro for igual!

[quote=RodrigoM91]Boa noite pessoal,

No livro SCJP 6, no capítulo de genéricos e coleções, página 327, é dada as seguintes classes:


public class Dog {

	public String name;
	
	public Dog(String n) {
		this.name = n;
	}
	
	@Override
	public boolean equals(Object obj) {
		if ((obj instanceof Dog) && ((Dog) obj).name == name) {
			return true;
		} else {
			return false;
		}
	}
	
	@Override
	public int hashCode() {
		return name.length();
	}
}
public class Cat {}

import java.util.HashMap;
import java.util.Map;

public class MapTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Dog d1 = new Dog("clover");
		
		m.put(d1, "Dog key");
		m.put(new Cat(), "Cat key");
		
		System.out.println(m.get(d1));
		System.out.println(m.get(new Cat()));
	}

}

O livro diz que a saída será:
Dog key
null

O que está correto, o que eu não estou entendo é a explicação que o livro dá:
“O ponto importante aqui é que o método get() não conseguiu encontrar o objeto Cat que foi inserido anteriormente…
…Por que foi possível usar uma instancia de Dog como chave, enquanto que usar uma instancia de Cat como chave não teve sucesso?
É fácil ver que Dog substituiu equals() e hashCode(), enquanto que Cat não o fez.”

Pra mim pelo que eu entendi o retorno é null porque são utilizadas duas referencias distintas de Cat, tanto que se alterar o trecho de código de modo a utilizar a mesma referencia de Cat tanto na inserção quanto na busca, o retorno será “Cat key”…

Nesse caso eu não estou entendendo bem ou no livro essa explicação não procede?

Valeu![/quote]

Veja bem, se neste exemplo vc sobrescrevesse a classe cat da mesma forma que faz com Dog?
No Dog vc sobrescreve o equals() e o hashcode() . Dentro do equals, que é usado pelo get(m.get(d1)). temos um teste onde ser pergunta se o objeto passado é uma instancia do Dog, então se o cat fosse implementado (com sobrescrita dos metodos equals e hashcode) da mesma forma ele passaria no teste (no if) pois um new cat() é uma instância de cat, ou de outra forma um new cat(“miau”) também passaria no teste pois este objeto é uma instância de cat. Assim de uma maneira geral ele diz que o cat retorna null porque não foi feita a sobrescrita dos métodos equals e hashcode.
Mas vc poderia colocar uma implementação diferente para esses métodos de forma que cada new cat() fosse diferente do outro, claro que respeitando as regras de sobrescritas desses métodos.

Boa tarde pessoal,

Entendi o que os três explicaram…!

Eu até posso então usar o objeto Cat como chave no map mais se torna ineficiente já que para recuperar eu teria que ter a mesma instancia do objeto, não é mesmo?

Exemplo:


import java.util.HashMap;
import java.util.Map;

class MapTest {
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Cat cat = new Cat();
		
		m.put(cat, "Cat key");
		
		System.out.println(m.get(cat));
}
}

O eficiente seria fazer conforme o livro da o exemplo do Dog que é implementar o método equals() e hashCode(), pra determinar em que situação dois objetos com instancias diferentes serão iguais e seus valores hash que serão usados para busca…

Certo?

Falo!

[quote=RodrigoM91]Boa tarde pessoal,

Entendi o que os três explicaram…!

Eu até posso então usar o objeto Cat como chave no map mais se torna ineficiente já que para recuperar eu teria que ter a mesma instancia do objeto, não é mesmo?

Exemplo:


import java.util.HashMap;
import java.util.Map;

class MapTest {
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Cat cat = new Cat();
		
		m.put(cat, "Cat key");
		
		System.out.println(m.get(cat));
}
}

O eficiente seria fazer conforme o livro da o exemplo do Dog que é implementar o método equals() e hashCode(), pra determinar em que situação dois objetos com instancias diferentes serão iguais e seus valores hash que serão usados para busca…

Certo?

Falo![/quote]
Eficiente e eneficiente não são as palavras corretas. A busca será tão eficiente no HashMap com a referência ou sem! O que ele quis colocar é a como diferenciar objetos! No caso do cachorro dois objetos são iguais se tiverem o mesmo nome. Porém, como foi implementado, eu posso ter dois cachorros diferentes porém com o mesmo hash, desde que o tenham os nomes tenham os mesmos números de letras!
A questão é que se você utilizar um hashMap ou set, ou qualquer outra forma de coleção com hash terá que implementar hashCode e equals. Na verdade não é correto utilizar um HashMap com objetos que não implementam hashcode equals

[quote=RodrigoM91]Boa tarde pessoal,

Entendi o que os três explicaram…!

Eu até posso então usar o objeto Cat como chave no map mais se torna ineficiente já que para recuperar eu teria que ter a mesma instancia do objeto, não é mesmo?

Exemplo:


import java.util.HashMap;
import java.util.Map;

class MapTest {
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Cat cat = new Cat();
		
		m.put(cat, "Cat key");
		
		System.out.println(m.get(cat));
}
}

O eficiente seria fazer conforme o livro da o exemplo do Dog que é implementar o método equals() e hashCode(), pra determinar em que situação dois objetos com instancias diferentes serão iguais e seus valores hash que serão usados para busca…

Certo?

Falo![/quote]

isso as suas dois questionamentos estão corretos.

[quote=x@ndy][quote=RodrigoM91]Boa tarde pessoal,

Entendi o que os três explicaram…!

Eu até posso então usar o objeto Cat como chave no map mais se torna ineficiente já que para recuperar eu teria que ter a mesma instancia do objeto, não é mesmo?

Exemplo:


import java.util.HashMap;
import java.util.Map;

class MapTest {
	public static void main(String[] args) {
		Map<Object, Object> m = new HashMap<Object, Object>();
		
		Cat cat = new Cat();
		
		m.put(cat, "Cat key");
		
		System.out.println(m.get(cat));
}
}

O eficiente seria fazer conforme o livro da o exemplo do Dog que é implementar o método equals() e hashCode(), pra determinar em que situação dois objetos com instancias diferentes serão iguais e seus valores hash que serão usados para busca…

Certo?

Falo![/quote]
Eficiente e eneficiente não são as palavras corretas. A busca será tão eficiente no HashMap com a referência ou sem! O que ele quis colocar é a como diferenciar objetos! No caso do cachorro dois objetos são iguais se tiverem o mesmo nome. Porém, como foi implementado, eu posso ter dois cachorros diferentes porém com o mesmo hash, desde que o tenham os nomes tenham os mesmos números de letras!
A questão é que se você utilizar um hashMap ou set, ou qualquer outra forma de coleção com hash terá que implementar hashCode e equals. Na verdade não é correto utilizar um HashMap com objetos que não implementam hashcode equals[/quote]

exato , terão o mesmo Hash caso tenham o mesmo tamanho do nome, apesar de não serem o mesmo objeto em relação ao equals que exige que tenham o mesmo nome para serem considerados iguais.

Blz Valeu, resolvido!