Dúvida questão certificado (equals)

O código abaixo gera como saída 3. Isso quer dizer que o método equals da classe WrappedString não está sendo chamado (porque se estivesse, acho que a saída deveria ser 2). Para provar isso, coloquei um “System.out.printf” no método.

import java.util.*;

public class WrappedString extends Object
{
	private String s;
	public WrappedString(String s){ this.s = s;}
	public static void main(String args[]){
		HashSet<Object> hs = new HashSet<Object>();
		WrappedString ws1 = new WrappedString("aardvark");
		WrappedString ws2 = new WrappedString("aardvark");
		String s1 = new String("aardvark");
		String s2 = new String("aardvark");

		hs.add(ws1);		hs.add(ws2);		hs.add(s1);		hs.add(s2);

		System.out.println( hs.size() );
	}

	@Override
	public boolean equals(Object o){
		System.out.printf("Chamou!\n");

		WrappedString outro = (WrappedString) o;

		if( s.equals(outro.s) ) return true;
		return false;
	}
}

A minha pergunta é: por que esse método equals que eu sobreescrevi na classe WrappedString não está sendo chamado?
Obrigado!

anote este metodo com @Override -> provavelmente vc esta sobrecarregando o metodo equals e ai vc não tem o comportamento esperado.

@Override public boolean equals(Object outro){ sysout("oi"); return false; }

Lembre-se de como é este método:

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#equals(java.lang.Object)

[quote=peczenyj]anote este metodo com @Override -> provavelmente vc esta sobrecarregando o metodo equals e ai vc não tem o comportamento esperado.

@Override public boolean equals(Object outro){ sysout("oi"); return false; }

Lembre-se de como é este método:

http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#equals(java.lang.Object)[/quote]

Não funcionou. Gerou o seguinte erro em tempo de compilação:

WrappedString.java:19: method does not override or implement a method from a supertype @Override ^ 1 error

Você não está sobreescrevendo o método equals, está sobrecarregando.

Entendi, realmente eu estava sobrecarregando ao invés de sobreescrever. Eu alterei meu código agora. Porém, ele continua gerando 3 na saída. Não era para gerar 2???

Era para dar erro. O HashSet usa o equals que recebe um objeto, não o que vc esta declarando :wink:

Como assim? Se eu já sobreescrevi o método equals então não deveria estar usando o método sobreescrito? Tem outra coisa estranha. Nem todos os objetos que colocarmos naquele HashSet poderão ter o cast do equals que eu sobreescrevi feito (pois nem todos os objetos são da classe WrappedString.

A minha pergunta agora é: qual equals a classe HashSet está chamando para ver se o elemento que eu estou inserindo já está no conjunto?

Olá amigo
eu testei aqui e quando eu sobescrevi o hashcode o código funcionou corretamente, mas msm assim me pareçe estranho funcionar só depois de sobescrever hashcode porq pelo oque eu sei ele testa o hashcode por último

import java.util.HashSet;

public class WrappedString  
{   
    private String s;   
    public WrappedString(String s){ this.s = s;}   
    public static void main(String args[]){   
        HashSet<Object> hs = new HashSet<Object>();   
        WrappedString ws1 = new WrappedString("aardvark");   
        WrappedString ws2 = new WrappedString("aardvark");   
        String s1 = new String("aardvark");   
        String s2 = new String("aardvark");   
  
        System.out.println(hs.add(ws1));        
        System.out.println(hs.add(ws2));        
        System.out.println(hs.add(s1));     
        System.out.println(hs.add(s2));   
  
        System.out.println( hs.size() );   
    }   
  
    public boolean equals(Object o){   
        System.out.printf("Chamou!\n");   
        
        if(o instanceof WrappedString) { 
        	
        	WrappedString outro = (WrappedString) o;   
        	if(this.s.equals(outro.s) ) 
        		return true;      
        }
        return false;
    }   
    
    public int hashCode() {
    	
    	return s.hashCode();
    }
}  

saída

  true
  Chamou!
  false
  true
  false
  2

Quer dizer então que a classe HashSet vai sempre chamar o equals da classe do objeto que está sendo inserido naquele momento?? No caso, na primeira inserção ela não chama nenhum equals porque ainda não existe nenhum elemento no conjunto. Na segunda, HashSet chama o equals que eu sobreescrevi (porque o objeto que está sendo inserido é do tipo WrappedString). E finalmente, na terceira e na quarta inserção, HashSet chama o equals da classe String. É isso?

sim
para funcionar certo vc vai ter que sobescrever o método equals e hashcode porq senão vc não vai conseguir impedir a inserção de objetos iguais…
porq a implementação padrão de equals simplesmente compara as referências de 2 objetos ou seja é semelhante ao operador ==

Bom, a regra prática, conforme K&B, Livro de Certificação, página 306, diz que o método equals() e hashcode() estão vinculados por um contrato de associação, portanto, você deve subscrever os dois métodos. Note que o método equals() não possiu implementação na classe Object, e que apenas o método hashcode() está implementado na mesma, assim, quando hashcode() não é sobreescrito em uma determinada classe não há polimorfismo e método original será chamado. Ocorre que o método original de hashcode() tem sua implementação específica, e isto significa que este pode retornar o MESMO valor para objetos considerados DIFERENTES. Por fim, o seu objeto WrappedString é quase sempre considerado diferente, já que o hashcode() original de Object foi chamado, pois retornou valores de hashing diferentes, o que permitiu a duplicata deste objeto no seu HashSet. Diferentemente do que ocorreu com os objetos String, uma vez que estes possuiem implementações apropriadas de hashcode() e equals().

Filipe A. Pinheiro

Pelo que sei ele, o Set chama primeiro o hashCode(), e neste caso objetos distintos podem ter o mesmo hashCode(), embora não é aconselhado por causa de performance.

Caso os objetos possuem o mesmo hashCode(), ai sim o Set, chama o equals() para verificar se realmente se os objetos são os mesmos.

Att

Bom!.. Simples! pelo que eu vi, vc não esta chamando o equals da classe que pretende sobrescreve-lo.
Vc esta chamando-o de uma classe String (variavel: “s”). s.equals(outro.s). Assim como o vmsb11 demonstrou, mas o problema não é solucionado após implementar hashcode e sim na forma de escrever equals (this.s.equals(outro.s)).

Grande abraço!

Dorpho acho que o problema foi resolvido com o hashCode sim, fiz os testes aqui e so foi verificado os objetos iguais quando eu sobreescrevi o hashCode

Td bem, acredito que tenha sido resolvido. Contudo, o primeiro código não é uma implementação correta.
É necessário o teste É-UM antes, pois caso não seja um objeto WrapperString ele usará o equals patrão do objeto String, sendo assim “Chamou!” é mostrado apenas uma vez, como foi mostrado anteriormente.

Deixa eu aproveitar o tópico e fazer uma pergunta, sobre o codigo abaixo:

[code]public static void main(String[] args) {
//new Cat().go(null);

	HashSet<Object> teste = new HashSet<Object>();
	teste.add(new Cat("everton"));
	teste.add(new Cat("notreve"));
	teste.add(new Cat("everton"));
	teste.add(new Cat("joao"));

}
	
	public boolean equals(Object o){
		System.out.printf("equals\n"); 
		if(o instanceof Cat && this.nome.equals(((Cat)o).nome))
		return true;
		return false;
	}
	public int hashCode(){ // método que olha o hash da variavel nome.
		System.out.printf("hash\n"); 
		return this.nome.hashCode();
	}[/code]

Pq o codigo abaixo imprime:

hash hash hash equals hash
Em todos objetos ele nao faz chamada a equals e hash não??

creio que seja porq na implementação do HashSet ele chama o hashcode do objeto que vc quer inserir e então ele só chamará o método equals quando vc encontrar um hashcode igual ao do objeto que vc quer inserir ai como são iguais ele chamará o método equals.

Mas vmsb “notreve” tem o mesmo hashCode que “everton” então o segundo objeto e o primeiro ele chamaria equals, e o terceiro e o segundo ele tb chamaria equals n?

aqui em casa não produziram o msm hashcode…
olha o teste que eu fiz:

  System.out.println("everton".hashCode());   
  System.out.println("notreve".hashCode());  
  System.out.println("everton".hashCode());
  System.out.println("joao".hashCode());  

saída

  -1376382091
  2129618293  
  -1376382091
  3267635 

na implementação de hashcode da class String deve levar em consideração a ordem dos digitos

puts fudeu entao…pq será que eles nao tem o mesmo hashCode?