Duvida com hashCode

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

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

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…)

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.

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:

[code]
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;
}
} [/code]

If com return true ou return false? Lembre-se o if avalia uma expressão booleana. Então, isso é o mesmo que:

[code]
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()) ...;

}[/code]

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á.

[quote=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:[/quote]

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

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:

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.

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!

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.

[quote=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[/quote]

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()) //....
}

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

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

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

[quote=Marcos Lima]Puts, ViniGodoy, vc está me destruindo…
Acho que vou ler um livro, hahaha…[/quote]

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).

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

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!

[quote=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-.[/quote]

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.

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.