Dúvida questão certificado (equals)

23 respostas
T

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!

23 Respostas

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)

T

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)

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

Jairo_Junior1

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

T

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???

peczenyj

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

T

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?

V

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
T

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?

V

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 ==

sech777

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

A

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

DorPho

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!

evertonsilvagomesjav

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

DorPho

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.

evertonsilvagomesjav

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

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();
		}
Pq o codigo abaixo imprime:
hash
hash
hash
equals
hash
Em todos objetos ele nao faz chamada a equals e hash não??
V

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.

evertonsilvagomesjav

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?

V

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

-[telefone removido]
  [telefone removido]  
  -[telefone removido]
  3267635

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

evertonsilvagomesjav

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

V

olha la na documentação do hashcode

Returns a hash code for this string. The hash code for a String object is computed as 
 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 using int arithmetic, where s[i] is the ith character of the string, n is the length of the string, and ^ indicates exponentiation. (The hash value of the empty string is zero.)

ou seja ele usa exponenciação em conjunto com o valor ascii do caracter na implementação ou seja imagine a string “ab” a impl possivelmente será hash = valorAsciiA * 31 ^ tamanho - 1 + valorAsciiB * 31 ^ tamanho - 2 onde ^ = exponenciação
então a string “ba” irá retornar um hashcode diferente porq no cálculo é relacionado o código ascii com a exponenciação porq a fórmula seria esta: hash = valorAsciiB * 31 ^ tamanho - 1 + valorAsciiA * 31 ^ tamanho - 2
conseguiu enxergar que o resultado será diferente
faça o calculo no papel que vc conseguirá enxergar
assuma que o valor ascii de a é 97 e b é 98 só para testar.
só corrigindo é hash = valorAsciiA * 31 ^ tamanho - 1 + valorAsciiB * 31 ^ tamanho - 2 em vez de hash = valorAsciiA * 31 ^ 0 + valorAsciiB * 31 ^ 1

T

Como assim everton? A idéia é que a função hash tenha o menor número possível de colisões mesmo. Eu entendi o seguinte:

  1. everton tenta entrar no conjunto. É calculado o hash. O hash não bate com nenhum elemento do conjunto (pois não há nenhum elemento no conjunto ainda). Logo, não é preciso compará-lo com equals. everton é adicionado no conjunto.

  2. notreve tenta entrar no conjunto. É calculado o hash. O hash não bate com o hash de everton, então, não é preciso chamar equals (os objetos já são diferentes). notreve é adicionado no conjunto.

  3. everton tenta entrar no conjunto. É calculado o hash. Já existe um elemento no conjunto que possui o mesmo hash do novo everton (esse elemento é o velho everton), logo, é necessário compará-los com equals também. O equals então é chamado e retorna verdadeiro. Logo, esse novo everton não é adicionado no conjunto.

  4. joão tenta entrar no conjunto. É calculado o hash. Os hash de joão não bate com o hash de everton nem com o de notreve. Logo, eles já são diferentes. Não é preciso compará-los com equals. joão é adicionado no conjunto.

V

exatamente isso TiagoTC por isso que o método hashcode utiliza exponenciação no calculo por isso que quando alteramos a ordem das strings o resultado será diferente

evertonsilvagomesjav

De acordo com as explicações que vcs deram eu entendi perfeitamente e agredeço por sanar minha duvida, agora se eu nao me engano ou eu nao entendi bem e vou ler de novo no livro da Kathy Sierra estava falando que o hashCode de “amy” e “may” seriam os mesmos agora não sei se entendi errado ou estava isso mesmo, chegando em ksa vou ler novamente, mas de qualquer forma vlwww pelas explicações!!!

Criado 6 de janeiro de 2010
Ultima resposta 7 de jan. de 2010
Respostas 23
Participantes 8