método hashCode () quando equals () é baseada em vários campos independentes

StackOverflow https://stackoverflow.com/questions/479105

  •  20-08-2019
  •  | 
  •  

Pergunta

Eu tenho uma classe cuja igualdade é baseada em 2 campos de tal forma que se qualquer um é igual, então os objetos desse tipo são considerados iguais. Como posso escrever uma função hashCode () para tal equals () para que o contrato geral de hashCode são iguais quando iguala retornos verdade é preservada?

public class MyClass {
  int id;
  String name;

  public boolean equals(Object o) {
    if (!(o instanceof MyClass))
      return false;
    MyClass other = (MyClass) o;
    if (other.id == this.id || other.name == this.name)
      return true;
    return false;
  }
}

Como faço para escrever uma função hashCode () para esta classe? e eu quero evitar o caso trivial aqui de retornar uma constante assim:

public int hashCode() {
  return 1;
}
Foi útil?

Solução

Eu não acho que existe um código hash não trivial. Além disso, o equals() viola o contrato geral como declarado no API --- é não transitivo :

(1,2) igual (1,3)

(4,3) igual (1,3)

Mas (4,3) é não igual para (1,2).


Por uma questão de exaustividade, eu apresento a vocês o Skeet - Niko prova =)

Reivindicação :. O hashcode deve ser a função constante trivial

Proof : Let (a,b) e (c,d) haver dois objetos com hashcodes distintos, ou seja h(a,b) ≠ h(c,d). Considere o objeto (a,d). Por definição do OP, (a,d) é igual a (a,b) e (a,d) é igual a (c,d). Resulta do contrato hashcode que h(a,d) = h(a,b) = h(c,d); uma contradição.

Outras dicas

Ok, em seu cenário, ignorando os requisitos de API para um segundo, não há nenhuma função hash não constante

Imagine que houvesse uma hashfunction que tem valores diferentes para

(a, b), (a, c), b! = C, então haxixe (a, b)! = Haxixe (a, c), eventhough (a, b) = (a, c).

De modo semelhante, (b, a) e (c, a) deve emitir o mesmo hashCode.

Vamos chamar nossos-função hash h. Encontramos:

h (x, y) = h (x, w) = h (v, w) forall x, y, v, w.

Assim, a única hashFunction que faz o que você quer é constante.

Eu tenho certeza que o direito de Zach -. Não há nenhuma hashcode não-trivial para fazer isso

Pseudo-proof:

Considere quaisquer dois valores não iguais, X = (ID1, NAME1) e Y = (ID2, NOME2).

Agora, considere Z = (ID2, name1). Isso é igual a X e Y, então deve ter o mesmo hashcode que ambos X e Y. Portanto X e Y devem ter o mesmo código hash -. Que meios todas valores devem ter o mesmo código hash

Há uma razão pela qual você tem em uma situação estranha - você está quebrando a natureza transitória de iguais. O fato de que x.equals (Z) e Z.equals (Y) deve significa que x.equals (Y) -, mas isso não acontece. Sua definição da igualdade não é adequado para o contrato normal, de iguais.

Eu acho que você não pode. A razão é, o seu método equals() não é transitiva.

meios para transitivity x três não-nulos, Y, Z, se x.equals(y), y.equals(z), então x.equals(z). No seu exemplo, um objeto x={id: 1, name: "ha"}, y={id: 1, name: "foo"}, z={id: 2, name: "bar"} têm essa propriedade (x.equals(y) and y.equals(z)). No entanto, x.equals(z) é false. Cada método equals() deve ter esta propriedade, consulte a documentação da API Java.

Voltar para funções hash: Cada função produz uma equivalência definida por f(x)==f(y). Isso significa que se você estiver interessado em comparação dos valores de função e deseja que ele retornar true se x==y (e, possivelmente, em outros casos), você vai receber uma relação transitiva, o que significa que você tem que considerar, pelo menos, um fechamento transitivo de objetos equivalência. No seu caso, o fechamento transitivo é a relação trivial (tudo é igual a qualquer coisa). Que significa que você não pode distinguir objetos diferentes por qualquer função.

Você definido intencionalmente igualdade como quando ids são iguais ou nomes são iguais .. Shouldnt o "OR" ser um "E"?

Se você quis dizer "E" então o seu código hash deve ser calculada utilizando o mesmo ou menos (mas nunca usar campos não utilizados pelos iguais) campos que são de equals ().

Se você quis dizer "OR", em seguida, você r hashgcode não deve incluir id ou nome no seu cálculo hashcode que doesnt realmente faz sentido.

EDIT:. Eu não li a questão com cuidado

-

Vou usar commons-lang jar.

obras

XOR os membros devem hashCode. Como deveriam implementos hashCode () e equals () corretamente.

No entanto, o código pode errado se você não proteger o seu hashCode. Uma vez que foi hash, ele não deve ser alterado. Deve ser impedido de ser acontecer.

public hashCode(){
   return new AssertionError();
}

ou

 public class MyClass {
   final int id;
   final String name;
   // constructor
 }

ou

public class MyClass {
   private int id;
   private String name;
   boolean hashed=false;
   public void setId(int value){
     if(hashed)throw new IllegalStateException();
     this.id=value;
   }
   public void setName(String value){
     if(hashed)throw new IllegalStateException();
     this.name=value;
   }
   // your equals() here
   public hashCode(){
     hashed=true;
     return new HashCodeBuilder().append(id).append(name).toHashCode();
   }
}

Depois de re-ler a pergunta.

Você pode auto-completar o outro campo quando um deles sendo atualizado.

-

EDIT:. Meu código pode dizer melhor do que o meu Inglês

void setName(String value){
  this.id=Lookup.IDbyName(value);
}
void setID(String value){
  this.name=Lookup.NamebyId(value);
}

EDIT 2:

O código em questão pode errado, como sempre retornará verdadeiro a menos que você defina o id & nome.

Se você realmente quer um método que faz iguais parciais, criar seu próprio API que o nome "partialEquals ()".

A rota mais simples é XOR os hashcodes de cada campo individual. Isto tem feiúra menor em algumas situações (por exemplo, em coordenadas X, Y, faz com que a potencialmente má situação de ter hashes iguais quando você virar X e Y), mas em geral, é bastante eficaz. Tweak conforme necessário para reduzir as colisões se necessárias para a eficiência.

Como sobre este

public override int GetHashCode()
{
    return (id.ToString() + name.ToString()).GetHashCode();
}

A função deve allways retornar um hash "válido" ...

Editar: só notei que você use "ou" não "e": P Bem, eu duvido que haja qualquer boa solução para este problema ...

Como cerca

public override int GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top