Como sobre-escrever corretamente hashCode?

Pessoal, lendo o livro da Khatty, me bateu essa dúvida. Ela só diz que o hashcode deve ser sobre-escrito, quando sobre-escrever equals, para algo que diferencie ao máximo objetos diferentes. Porem, nao diz como seria uma boa implementação de hashcode…

alguem poderia explicar isso?

Tnks…

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

Obrigado, vini!

[]s

Se quiser, implementei uma classe que facilita a construção de hashcodes, usando aquele algoritmo.

Se seu hashcode se basear em nome, idade e telefone, da classe contato, bastaria fazer:

public int hashCode() { return new HashBuilder(nome).add(idade).add(telefone).hashCode(); }

Está em anexo.

Muuuuito resumidamente, o contrato entre equals e hashCode diz o seguinte:

  1. Se a.equals(b), então a.hashCode == b.hashCode;
  2. Se !a.equals(b), então (a.hashCode == b.hashCode) || (a.hashCode != b.hashCode);
  3. Se a.hashCode == b.hashCode, então a.equals(b) || !a.equals(b);
  4. Se a.hashCode != b.hashCode, então a.equals(b) || !a.equals(b);

Só complementando, aconselho muito o livro do Joshua Bloch, Effective Java. Nele o autor descreve como reescrever os métodos de Object, como tratar exceções, como lidar com Generics, Threads, entre outros assuntos. É leitura obrigatória!

É o mesmo que recomendo no link, que passei ali em cima. :slight_smile:

Aliás, já teve acesso a última edição?

Para quem gosta de C++, existem livros similares, os chamados Effective C++, More Effective C++ e Effective STL, todos do Scott Meyers. Ele foi realmente o criador da série Effective. Também são leitura obrigatória.

[quote=pango]Muuuuito resumidamente, o contrato entre equals e hashCode diz o seguinte:

  1. Se a.equals(b), então a.hashCode == b.hashCode;
  2. Se !a.equals(b), então (a.hashCode == b.hashCode) || (a.hashCode != b.hashCode);
  3. Se a.hashCode == b.hashCode, então a.equals(b) || !a.equals(b);
  4. Se a.hashCode != b.hashCode, então a.equals(b) || !a.equals(b);[/quote]

Mais resumidamente ainda:

-) Se a.equals(b), então a.hashCode == b.hashCode;

O resto são derivações.

[quote=ViniGodoy]Se quiser, implementei uma classe que facilita a construção de hashcodes, usando aquele algoritmo.

Se seu hashcode se basear em nome, idade e telefone, da classe contato, bastaria fazer:

public int hashCode() { new HashBuilder(nome).add(idade).add(telefone).hashCode(); }

[/quote]

Muito legal. Se me permite a sugestão eu tirava esses vários construtores e modificava para implementar o padrão builder, algo assim

// uso public int hashCode() { return HashBuilder.add(nome).add(idade).add(telefone).hashCode(); }


class HashBuilder

public interface Hash{
    int hashCode();
   // todos os métodos add de objeto, etc... 
}

private static class BuildedHash implements Hash{
          
     // atributos 
     public BuildedHash (){}

     // implementação de todos os métodos add de objeto, etc... 
    // exemplo
    public Hash add(int integer(){
     // código de adição desde hash
   }
    
}
// adds estáticos equivalentes aos construtores , exemplo
 
public static Hash add(Object obj){
      return new BuildedHash ().add(obj);
}
   
public static Hash add(int integer){
      return new BuildedHash ().add(integer);
}

Só para simplificar a invocação e ficar no padrão builder.

Não vi vantagem nenhuma nesse caso.

O padrão Builder é mais usado para desacoplar a forma que um objeto complexo é construído da forma como ele é representado. Nesse caso, o objeto nem é complexo. Bem pelo contrário, é bastante pequeno e coeso, tanto que a classe não sofre alteração a vários anos.

Também não vi vantagem em fazer um Builder que nada mais é do que uma interface estática para o construtor. Iria estar tão cheio de adds quanto a classe de Hash está cheia de construtores hoje em dia, só que eu teria o inconveniente de ter duas classes para manter ao invés de uma só.

Você poderia me dizer uma única vantagem de usar esse padrão nesse caso, que não fosse “estar no padrão”?
E onde simplifica a invocação? O exemplo que vc demonstrou é tão complexo quanto (embora nenhum dos dois códigos seja realmente complexo).

Anyway, vejo mais sentido em usar o padrão strategy no futuro, caso eu fosse implementar mais de uma política de hash. Mas até hoje nunca precisei disso.

[quote=ViniGodoy]
Você poderia me dizer uma única vantagem de usar esse padrão nesse caso, que não fosse “estar no padrão”?
E onde simplifica a invocação? O exemplo que vc demonstrou é tão complexo quanto (embora nenhum dos dois códigos seja realmente complexo).

Anyway, vejo mais sentido em usar o padrão strategy no futuro, caso eu fosse implementar mais de uma política de hash. Mas até hoje nunca precisei disso.[/quote]

Foi só uma sugestão. a chamada é simplificada porque não preciso invocar nenhum construtor explicitamente (IoC).
Se vc quiser ter uma ou mais formas de algoritmo de Hash basta criar diferentes implementações de Hash e não de HashBuilder.
Por exemplo, se só deu um add o hash pode ser o próprio valor de hash passado sem precisar nada mais (equivale a um returna name.hashCode da vida).

Como falei, era apenas uma sugestão…

Sim, eu entendi. E nem fiquei nervoso. Só queria ver se vc via alguma vantagem que eu não estava vendo.

valeu pela sugestão. :wink: