Duvida com hashCode

30 respostas
renato_ramos

Oi!

eu estava fazendo uns exercicios da faculdade pediram para eu gerar o hash da minha classe, como eu não entendi muito bem
eu pedi para o netbeans gerar para mim.

Até td bem ele gerous mas não funcionou.

public class Aluno implements Comparable{
    private String nome, turno, semestre;
    private int matricula;

    public Aluno() {
        setNome("");
        setTurno("");
        setSemestre("");
        setMatricula(0);
    }

//gets e sets

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 17 * hash + (this.nome != null ? this.nome.hashCode() : 0);
        hash = 17 * hash + (this.turno != null ? this.turno.hashCode() : 0);
        hash = 17 * hash + (this.semestre != null ? this.semestre.hashCode() : 0);
        hash = 17 * hash + this.matricula;
        return hash;
    }

essa eh minha classe abaixo como eu testei.

public class TesteHashSet {
    public static void main(String[] args){
        Set<Aluno> conjunto = new HashSet<Aluno>();

        Aluno a1 = new Aluno();
        a1.setNome("Renato");
        a1.setTurno("noite");
        a1.setMatricula(95);
        a1.setSemestre("semestre");

        Aluno a2 = new Aluno();
        a2.setNome("Renato");
        a2.setTurno("noite");
        a2.setMatricula(95);
        a2.setSemestre("semestre");

        conjunto.add(a1);
        conjunto.add(a2);

        System.out.println(conjunto);
    }
}

alguem me explica pq não funcionou.
muito obrigado =D

30 Respostas

ViniGodoy

http://www.guj.com.br/posts/list/52485.java#276120

M

O que não funcionou, qual foi o erro?

O código do hashCode() parece bom, está levando em consideração o nome, turno, semestre e matrícula (Se bem que acho que se levasse só a matrícula em consideração seria melhor…)

ViniGodoy

Também é necessário implementar o método equals. Você sempre deve sobrescrever o par, equals e hashcode, para que as collections funcionem de maneira consistente.

M

Exatamente, se vc não implementar o equals não vai funcionar.

Tente assim:

public boolean equals(Aluno a) { if (getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //.... { return true; } else { return false; } }

E quanto ao hashCode, vc poderia levar em consideração só a matrícula (Não existem dois alunos com a mesma matrícula), então eu faria assim:

public int hashCode() { return getMatricula().hashCode(); }

Bem mais simples, funcional e de fácil manutenção :lol:

ViniGodoy
public boolean equals(Object a) {
    if (o == null) return false;
    if (o == this) return true;
    if (a.getClass() != getClass()) return false;
    Aluno a = (Aluno) o;

    if (getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //....  
   {  
       return true;  
   }  
   else  
   {  
       return false;  
   }
}
If com return true ou return false? Lembre-se o if avalia uma expressão booleana. Então, isso é o mesmo que:
public boolean equals(Object o) {
    if (o == null) return false;
    if (o == this) return true;
    if (a.getClass() != getClass()) return false;
    Aluno a = (Aluno) o;

    return getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) ...;
}

Outra coisa, o método equals aceita um Object como parâmetro, não um Aluno. Se fizer com aluno, você estará fazendo a sobrescrita do método, não dará erro e não funcionará.

fitoplancton

Marcos Lima:
Exatamente, se vc não implementar o equals não vai funcionar.

Tente assim:

public boolean equals(Aluno a) { if (getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //.... { return true; } else { return false; } }

E quanto ao hashCode, vc poderia levar em consideração só a matrícula (Não existem dois alunos com a mesma matrícula), então eu faria assim:

public int hashCode() { return getMatricula().hashCode(); }

Bem mais simples, funcional e de fácil manutenção :lol:

Você não pode estar falando sério… :shock:

M

Nossa, fui noob com esse if, haha :stuck_out_tongue:

E quanto ao equals… Certo, é Object e também esqueci desses ifzinhos testando se é null, e tal…

Dá um desconto, hoje é sexta :roll:

M

fitoplancton, no hashCode eu falei sério… Se vc levar em conta todos os campos, vc pode acabar com 2 Alunos com mesma matrícula em cursos diferentes, o que com certeza é um problema. Mas isso se aplicaria ao método equals também. Lembrando que aí é questão de negócio e estrutura do software, e não de desempenho.

renato_ramos

Opa, desde ja agradeco a atenção

eu tentei

getMatricula().hashCode(); e não afuncionou!

entao eu deixei somente getMatricula();

funcionou!

mas isso foi somente exemplo e tals… e gostaria de saber por que com as string como parametro do hash não funcionou já que eu coloquei tudo igual.

qnto ao equals(Aluno a); eu implementei mas o netbeans fala para ter um equals(Object obj);

deixei o do Object obj;

obrigado!

M

Pelo jeito vc está usando Java 1.4…

Para isso, tente

return new Integer(getMatricula()).hashCode();

Usar a própria matrícula como hashCode é uma saída, mas se vc tem muitos Alunos para cadastrar pode haver muita colisão no seu HashSet.

Não funcionou antes porquê:
O hashCode que estava sendo chamado era herdado da classe Object. Se vc observar, ele é nativo e não leva os atributos do objeto herdado em questão (Até pq não teria como, ele não os conhece). Mesmo que todos os atributos sejam iguais, ele não vai retornar o mesmo hashCode, você sempre deve sobrescrevê-lo quando usar como índice para qualquer tabela Hash.

Use o equals sugerido pelo ViniGodoy, eu não estava pensando bem quando falei para usar Aluno como parâmetro :lol:
Espero que eu tenha sido claro.

sergiotaborda

renato_ramos:

essa eh minha classe abaixo como eu testei.

public class TesteHashSet {
    public static void main(String[] args){
        Set<Aluno> conjunto = new HashSet<Aluno>();

        Aluno a1 = new Aluno();
        a1.setNome("Renato");
        a1.setTurno("noite");
        a1.setMatricula(95);
        a1.setSemestre("semestre");

        Aluno a2 = new Aluno();
        a2.setNome("Renato");
        a2.setTurno("noite");
        a2.setMatricula(95);
        a2.setSemestre("semestre");

        conjunto.add(a1);
        conjunto.add(a2);

        System.out.println(conjunto);
    }
}

alguem me explica pq não funcionou.
muito obrigado =D

Se vc explicar o que significa “não funcionar” …

O problema não é o seu hash é o seu conceito.

O hash está ok mas vc está colocando dois objetos iguais num Set. O set vai guardar o primeiro.
Quando tentar por o segundo ele vai encontrar um com o mesmo hash. (acontece uma colisão de hash)
Então ele vai usar o equals() para saber se o objeto é o mesmo. ah!, mas vc não implemento o equals, logo o equals padrão vai ser usado.
Este vai dizer que os objetos são diferentes ( pois ele apenas faz == entre eles). E portanto o Set aceita o segundo objeto.

O set tem razão. A sua classe é que está errada :slight_smile:

implemente o equals em conformidade com o hashSet.

Agora, o hash não precisa ter todos os campos da classe. Isso representa que vc não sabe o que é e para que serve o hash.

A regra é assim :

  1. se x.equals(y) => x.hashCode() == y.hashCode()
  2. Se reimplementar equals ou hashCode tem que reimplementar o outro também.

Atendamos a (1) apenas, pois já vimos que o seu problema é não ter respeitado (2)
Vamos pensar que para que os objetos sejam iguais os campos A e B tem que ser iguais em ambos os objetos

Portanto, se A E B têm que ser iguais é verdade que A tem que ser igual, logo se usar o hashcode como A.hashCode vc está garantindo (1) e o hash estará bem implementado.

Alguem ai sugeriu usar apenas matricula.hashCode, isso está correto sim.

Mas porque escolher A em vez de B para ser usado no hash? Vc deve escolher aquele que tiver probabilidade maior de ser diferente. A matricula é o que tem maior probabilidade porque: ha muitos alunos num turno e semestre e é possivel ter pessoas com o mesmo nome.
Na construção do equals o campo que tiver mais probabilidade de ser diferente tb deve vir primeiro.

Já agora , nunca , nunca, nunca, (…) , nunca , nunca façam um if/else que retorna boleano

Isto

public boolean equals(Aluno a)
{
    if (getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //....
    {
        return true;
    }
    else
    {
        return false;
    }
}

Se substitui por :

public boolean equals(Aluno a)
{
    return getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //....
}
ViniGodoy

O hashCode do Integer é o próprio int que ele contém. Então:
new Integer(getMatricula()).hashCode() é equivalente a só getMatricula().

M

Puts, ViniGodoy, vc está me destruindo…
Acho que vou ler um livro, hahaha…

Felagund

Vejo algum null pointer a caminho em qualquer uma dessa condições :slight_smile:

fitoplancton

Marcos Lima:
Puts, ViniGodoy, vc está me destruindo…
Acho que vou ler um livro, hahaha…

Recomendo você ler algum livro para conhecer um pouco mais da linguagem Java…

Um muito bom é o “Use a Cabeça! Java” (Head First! Java).

M

fitoplancton,

“Adorei” a sua piada, mas eu já li esse livro, achei ele muito bom, e foi justamente por isso, somente com o intuito de ajudar, que falei isso para o Alisson-.

renato_ramos

minha classe inteira

public class Aluno implements Comparable{
    private String nome, turno, semestre;
    private int matricula;

    public Aluno() {
        setNome("");
        setTurno("");
        setSemestre("");
        setMatricula(0);
    }

    public int getMatricula() {
        return matricula;
    }

    public void setMatricula(int matricula) {
        this.matricula = matricula;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getSemestre() {
        return semestre;
    }

    public void setSemestre(String semestre) {
        this.semestre = semestre;
    }

    public String getTurno() {
        return turno;
    }

    public void setTurno(String turno) {
        this.turno = turno;
    }

 
    @Override
    public boolean equals(Object obj)
    {
        if(obj == null){
            return false;
        }

        Aluno a = (Aluno)obj;
        return (!(obj == null)) &&
                getNome().equals(a.getNome()) &&
                getSemestre().equals(a.getSemestre()) &&
                getTurno().equals(a.getTurno());
    }

    @Override
    public String toString(){
        String string = "Nome : " + getNome() +
                        "Turno: " + getTurno() +
                        "Semestre: " + getSemestre() +
                        "Matricula: " + getMatricula();
        return string;
    }

    @Overrride
    public int compareTo(Object o) {
        Aluno outroAluno = (Aluno) o;
        if(outroAluno.getMatricula() > this.matricula)
            return 1;
        else if(outroAluno.getMatricula() == this.matricula)
            return 0;
        else
            return -1;
    }

    @Override
    public int hashCode() {
        return new Integer(getMatricula()).hashCode();
    }
}

o meu nao funciona era que estava colcando os dois dentro do Set sendo que tinham todos os atributos iguais
em nenhum momento eu falei que eu dominava o conceito de hash ^^’

com os atributos strings ele adiciona os dois objetos… só queria entender pq. oq estaria errado

valeus!

fitoplancton

Marcos Lima:
fitoplancton,

Adorei a sua piada, mas eu já li esse livro, achei ele muito bom, e foi justamente por isso, somente com o intuito de ajudar, que falei isso para o Alisson-.

Tudo bem. :slight_smile:

Só vamos tentar resolver os problemas dos nossos membros de um jeito simples ou com fontes c/ exemplos online ao invés de mandar ler um livro. :roll:

Grato,
Fitoplancton.

ViniGodoy

Eu recomendo a leitura do Effective Java, capítulo 3. Ela pode ser acessada no site:
http://developer.java.sun.com/developer/Books/effectivejava/Chapter3.pdf

Se possível, comprem o livro e leiam inteiro. É uma leitura praticamente obrigatória para quem quer programar bem em Java.

sergiotaborda

Felagund:
sergiotaborda:

Já agora , nunca , nunca, nunca, (…) , nunca , nunca façam um if/else que retorna boleano

Isto

public boolean equals(Aluno a)
{
    if (getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //....
    {
        return true;
    }
    else
    {
        return false;
    }
}

Se substitui por :

public boolean equals(Aluno a)
{
    return getNome().equals(a.getNome()) && getTurno().equals(a.getTurno()) //....
}

Vejo algum null pointer a caminho em qualquer uma dessa condições :)

No caso de um bean como este sim vc precisa de verificação de null.
Mas esse não era o ponto … o ponto era o estilo de programação aprendiz de feiticeiro do primeiro codigo :slight_smile:

renato_ramos

ops copiar e colar eh uma desgraça… já arrumei as coisas gritantes que estavam ai ^^’

muito obrigado =D

M

Valeu ViniGodoy, vou dar uma olhada!
Tudo o que vc falou eu já tinha aprendido, mas é complicado lembrar… Espero que o renato_ramos esteja se achando no meio desse tópico =P

M

sergiotaborda… Aprendiz de feiticeiro?

Foi só uma programação leviana. Rs…

ViniGodoy

Marcos Lima:
Puts, ViniGodoy, vc está me destruindo…
Acho que vou ler um livro, hahaha…

Nada pessoal, entretanto. heheheehh

Eu é que sou chato mesmo. Principalmente no Java Básico.

AlexMercer

ahaha

M

Que diabos…?

renato_ramos

Marcos Lima:
Valeu ViniGodoy, vou dar uma olhada!
Tudo o que vc falou eu já tinha aprendido, mas é complicado lembrar… Espero que o renato_ramos esteja se achando no meio desse tópico =P

sim sim me achei agora está assim e funcinou, só adiciono um objeto só

public class Aluno implements Comparable{
    private String nome, turno, semestre;
    private int matricula;

    public Aluno() {
        setNome("");
        setTurno("");
        setSemestre("");
        setMatricula(0);
    }

//gets e sets.

 
    @Override
    public boolean equals(Object obj)
    {
        Aluno a = (Aluno)obj;
        return (!(obj == null)) &&
                getNome().equals(a.getNome()) &&
                getSemestre().equals(a.getSemestre()) &&
                getTurno().equals(a.getTurno());
    }

    @Override
    public String toString(){
        String string = "Nome : " + getNome() +
                        "Turno: " + getTurno() +
                        "Semestre: " + getSemestre() +
                        "Matricula: " + getMatricula();
        return string;
    }
    
    @Override
    public int compareTo(Object o) {
        Aluno outroAluno = (Aluno) o;
        if(outroAluno.getMatricula() > this.matricula)
            return 1;
        else if(outroAluno.getMatricula() == this.matricula)
            return 0;
        else
            return -1;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + (this.nome != null ? this.nome.hashCode() : 0);
        hash = 97 * hash + (this.turno != null ? this.turno.hashCode() : 0);
        hash = 97 * hash + (this.semestre != null ? this.semestre.hashCode() : 0);
        hash = 97 * hash + this.matricula;
        return hash;
    }


}
sergiotaborda

renato_ramos:
minha classe inteira

Tem mais um problema ai. O equals deve testar se a classe passada é compativel

@Override
    public boolean equals(Object obj)
    {
        if( !( obj instanceof Aluno)l){
            return false;
        }

        Aluno a = (Aluno)obj;
        return (!(obj == null)) &&
                getNome().equals(a.getNome()) &&
                getSemestre().equals(a.getSemestre()) &&
                getTurno().equals(a.getTurno());
    }


}

O instanceof retorna falso se o objeto não for da classe. Se for null, ele obviamente não é da classe.
Sem esta verificação não é licito fazer o cast mais em baixo podendo levar um ClassCastException e essa exceção não é primitida para o método equals (ver no javadoc).

renato_ramos

Valeu sergio!

agora surgiu outra questao lol

colocando somente a matricula no hash… agora está adicionando dois objetos com proriedades diferentes no hashset \o/

aaa negocio complicado !!

vo batalhando contra essa coisa aki xD

muito obrigado pela atenção messmo!

renato_ramos

desculpem o double post.

mas acho que entendi me acertem se estiver errado xD

na hora de adicionar no hashset é executado o hashCode do objeto se for igual … ele verifica se é igual mesmo com o equals()

por isso os dois tem que ser implementados com os mesmos paremetros.

tah certo??

obrigaddoo!!

Criado 12 de fevereiro de 2010
Ultima resposta 12 de fev. de 2010
Respostas 30
Participantes 7