[SCJP] dúvida hashCode() e equals

Olá, estou com uma dúvida com relação a esses dois métodos.

Se por exemplo eu usar uma classe chamada Person em uma Collection HashSet

E sobrescrever apenas o método equals, ela não terá efeito, ou seja, poderei inserir objetos repetidos…
Observem o código

[code]import java.util.HashSet;

public class PersonTest {
public static void main(String[] args) {
HashSet ps = new HashSet();

	ps.add(new Person("a"));
	ps.add(new Person("a"));
	ps.add(new Person("a"));		
	
	System.out.println(ps.size());
}

}

class Person {
private String name;
private int age;

public Person(String name) {
	this.name = name;
}

/*public int hashCode(){
	System.out.println("h1");
	return (int)Math.random()*1000;
}*/
public boolean equals(Object o) {
	if (!(o instanceof Person))
		return false;
	Person p = (Person) o;
	return p.name.equals(this.name);
}

}[/code]
O programa acima retornará 3…

Agora se eu descomentar o método hashCode(), ele retornará 1.
Não entendi como isso funciona…

Não entendi porque o método equals() só funciona quando eu sobrescrevo o método hashCode()

Alguém poderia me ajudar?

Grato.

Imagine que vc tenha 5 baldes

|| || || || |__|

E que vc irá guardar bolas nesses baldes e depois recuperar…

Cada bola tem uma cor e um número… e o número tem relacao com a cor… Se o número da bola dividido por 5 der como resto 1… será amarelo… Se o número dividido por 5 der como resto 2 a cor será azul…

Para facilitar o seu trabalho… vc vai dividir a bola nos baldes entre cores… então todas as amarelas vc coloca no primeiro balde… todas as azuis no segundo… todas as vermelhas no terceiro… etc

Quando vc for retirar alguma bola do balde… vc irá por exemplo verificar qual é a cor… entao… se precisar retirar alguma amarela… vc vai procurar apenas no primeiro balde…

Voce nao deve guardar bolas repetidas…

Entao, ao adicionar uma bola, vc irá verificar se já existe alguma bola com o mesmo número… no balde que deseja inserir… (vc nao precisará procurar nos outros baldes pois terá certeza que aquela cor nao estará num balde diferente)

Só que vamos supor que a bola número 6 que deveria ser amarela … está com a cor azul… vc irá inserir a bola 6 azul… mesmo já existindo uma bola 6 amarela… pq vc só irá verificar o balde com as azuis… e sendo assim terá duas bolas 6… uma amarela e uma azul…

Os baldes nesse exemplo… sao como a tabela Hash… cada possiçao é um número hash

As cores sao como hash codes… cada hashcode irá para um balde diferente

E por fim o número da bola é seu valor… que vc irá compar com o equals…

Se vc criar 3 objetos… com valores iguais… ou seja… com o método equals tendo resultado como true… e nao sobrescrever o hashcode corretamente… o que vai acontecer é que vc terá 3 objetos com equals true… mas hashcodes diferentes…
O que fará com que cada objeto fique num balde diferente… resultando em valores duplicados…


No caso das bolas é como se vc tivese 3 bolas com o valor 4… mas cada uma com cor diferente…

Resultado… cada bola irá para um balde… na hora que vc for verificar uma bola se já existe… vc verificará somente em um balde… resultando em valores duplicados…


Para resolver isso… o hashcode tem que ser condizente com a implementacao de equals… ou seja… se dois objetos tiverem como equals o resultado true… os hascodes deles tem que ser iguais (para ficarem no mesmo balde)

entendeu?

Pelo o que eu li na documentacao, a classe HashSet utiliza a classe HashMap em sua implementacao. Isso explica esse comportamento!

oi gente, qual a diferença:

[code]public static void main(String args[]){ 
	HashSet<String> lista = new HashSet<String>();
		lista.add("André");
	    lista.add("Luis");
	    lista.add("Araújo");
		lista.add("André");
	    lista.add("Luis");
	    lista.add("Araújo");
	    	System.out.println(lista);
	}[/code]

[code]	HashSet<String> lista = new HashSet<String>(); 	[/code]

ou

	[code]	Set<String> lista = new HashSet<String>();[/code]

[quote]Imagine que vc tenha 5 baldes

|| || || || |__|

E que vc irá guardar bolas nesses baldes e depois recuperar…

Cada bola tem uma cor e um número… e o número tem relacao com a cor… Se o número da bola dividido por 5 der como resto 1… será amarelo… Se o número dividido por 5 der como resto 2 a cor será azul…

Para facilitar o seu trabalho… vc vai dividir a bola nos baldes entre cores… então todas as amarelas vc coloca no primeiro balde… todas as azuis no segundo… todas as vermelhas no terceiro… etc

Quando vc for retirar alguma bola do balde… vc irá por exemplo verificar qual é a cor… entao… se precisar retirar alguma amarela… vc vai procurar apenas no primeiro balde…

Voce nao deve guardar bolas repetidas…

Entao, ao adicionar uma bola, vc irá verificar se já existe alguma bola com o mesmo número… no balde que deseja inserir… (vc nao precisará procurar nos outros baldes pois terá certeza que aquela cor nao estará num balde diferente)

Só que vamos supor que a bola número 6 que deveria ser amarela … está com a cor azul… vc irá inserir a bola 6 azul… mesmo já existindo uma bola 6 amarela… pq vc só irá verificar o balde com as azuis… e sendo assim terá duas bolas 6… uma amarela e uma azul…

Os baldes nesse exemplo… sao como a tabela Hash… cada possiçao é um número hash

As cores sao como hash codes… cada hashcode irá para um balde diferente

E por fim o número da bola é seu valor… que vc irá compar com o equals…

Se vc criar 3 objetos… com valores iguais… ou seja… com o método equals tendo resultado como true… e nao sobrescrever o hashcode corretamente… o que vai acontecer é que vc terá 3 objetos com equals true… mas hashcodes diferentes…
O que fará com que cada objeto fique num balde diferente… resultando em valores duplicados…


No caso das bolas é como se vc tivese 3 bolas com o valor 4… mas cada uma com cor diferente…

Resultado… cada bola irá para um balde… na hora que vc for verificar uma bola se já existe… vc verificará somente em um balde… resultando em valores duplicados…


Para resolver isso… o hashcode tem que ser condizente com a implementacao de equals… ou seja… se dois objetos tiverem como equals o resultado true… os hascodes deles tem que ser iguais (para ficarem no mesmo balde) [/quote]

Ok. E…?

Cara, sinceramente, ja que voce esta decidido a se dedicar a java, eu recomendo que voce pegue um livro e mandar ver nos estudos. Nao me leve a mal, mas respondi a mais de um post seu e notei que essa eh uma necessidade sua!

T+

Voce entendeu o que eu falei?

O porque vc tem que implementar o hashcode?

Se nao seus objetos vao ficar espalhados de forma incorreta na tabela?

Cara…

Para vc usar qualquer lista que implemente a interface Set, necessintam que vc implemente equals e hashCode, justamente porque elas como o nome diz ela, usa codigo hashing.

Por isso não funcionou…

E ira retornar sempre 1, pq Math.random sempre retorna um numero entre 0, 0.5, acredito que esse seu metodo hashCode não ta tão lega, recomento que de uma lida na

documentação para buscar melhores referências…

[quote=vitorfarias]Cara…

Para vc usar qualquer lista que implemente a interface Set, necessintam que vc implemente equals e hashCode, justamente porque elas como o nome diz ela, usa codigo hashing.

Por isso não funcionou…

E ira retornar sempre 1, pq Math.random sempre retorna um numero entre 0, 0.5, acredito que esse seu metodo hashCode não ta tão lega, recomento que de uma lida na

documentação para buscar melhores referências…

[/quote]

Alguns ajustes técnicos apenas…

Para você usar qualquer coleção que implemente a interface Set (se nao pode confundir com a interface list…)

Melhorando um pouco esse raciocínio… Se você sobrescreveu o equals obrigatoriamente tem que sobrescrever o hashcode (e sobrescrever de acordo com as regras do método equals e hashCode…)

Irá retornar sempre 0… e Math.random sempre retorna um número entre 0 e 1 (nao incluindo o 1)

[quote=andredecotia]oi gente, qual a diferença:

[code]	HashSet<String> lista = new HashSet<String>(); 	[/code]

ou

	[code]	Set<String> lista = new HashSet<String>();[/code]

[/quote]

Em um a sua variavel lista é do tipo HashSet… no outro é do tipo Set :lol:

[quote]Voce entendeu o que eu falei?

O porque vc tem que implementar o hashcode?

Se nao seus objetos vao ficar espalhados de forma incorreta na tabela?[/quote]

Acho que não compreendeu corretamente a pergunta…
Quero saber qual método válida se os dois métodos foram sobrescritos (ou seja lá como isso é validado)…

Fala andredecotia, blz?
Ow essa pergunta é sobre polimorfismo, não tem muita ver o tópico.
Rola mover para outro tópico?

Abs.

[quote=gervas-IO][quote]Voce entendeu o que eu falei?

O porque vc tem que implementar o hashcode?

Se nao seus objetos vao ficar espalhados de forma incorreta na tabela?[/quote]

Acho que não compreendeu corretamente a pergunta…
Quero saber qual método válida se os dois métodos foram sobrescritos (ou seja lá como isso é validado)…

[/quote]

Eu entendi o seu problema… voce nao compreendeu o que eu falei

O equals e o hascode trabalham juntos… nao é um ou outro… são os dois

Ok jovem, eu compreendi o que você falou, tb agradeço sua prestatividade!

Estou tentado entender como ocorre essa chamada

O HashSet vai e chama o hashCode de um objeto. Ok.
Se caso eu sobrescreva somente o método equals() e não o hashCode(), o equals() não vai ser chamado… mas porquê???

Se lá por meio do código ocorre a chamada:
a.equals(b)

a.equals() => terá que chamar o método sobrescrito… mas não chama!!!

Não quero saber sobre a funcionalidade da inserção em tabelas Hash, somente isso que eu quero saber…

[quote]
O equals e o hascode trabalham juntos… nao é um ou outro… são os dois[/quote]
Como isso ocorre???

Mas cara, se vc não entendeu deixa pra lá, blz?

hehehehe… entendi sim… afinal já tive a mesma dúvida algum dia…

O caso é o seguinte… se vc sobrescrever o método equals e nao o hascode … o equals será chamado…

Só que o equals só será chamado nos objetos que tiverem o mesmo hashcode (na analogia da bola… o equals só será chamado para as bolas da mesma cor)

Sacou??

Primeiro é feito o hashcode do objeto… e entao o equals só será chamado pra quem tiver o mesmo hashcode…

Por isso que… se a.equals(b) for true… a.hasCode() == b.hashCode()

No seu caso vc acha que o equals nao tá sendo chamado… pq ele deixou duplicados num foi isso? Na verdade o equals foi chamado sim… mas apenas nos objetos com mesmo hashcode… como cada objeto seu tinha um hashcode diferente… o HashSet já considerou que eram objetos diferentes…

Isso é para ter performance…

O hashcode é muito mais barato que um equals…

Entao vc faz um hash e depois compara com equals apenas os objetos com o mesmo hash…

Esclareceu?

O problema é que vc achou que o algoritmo era diferente se vc sobrescrever o equals e/ou o hashCode…

Mas o algoritmo do hash nao muda…

Só que ele fica falho se vc nao seguir o protocolo…

Aí é que está cerne da questão:

No código que postei insira a linha System.out.println(“equals”); no método equals, e verá que ela não é executada… Tudo isso que você disse faz muito sentido, porém pra mim se ele não executa a linha não faz sentido que esteja chamando o método…

Sacou?

Pois entao… o equals só será chamado para objetos com hashcode iguais… como tinha dito

Se o hashcode for diferente… o HashSet nao se dará ao trabalho de chamar o equals…

Caraca…
Entendi!

Vlw!

hehehe… blza

flw