hashcode () 메소드 ()가 여러 독립 필드를 기반으로하는 경우
문제
나는 평등이 2 개의 필드를 기반으로하는 클래스가있어 어느 쪽이든 동일하면이 유형의 객체가 동일하게 간주됩니다. equals ()에 대한 hashcode () 함수를 어떻게 작성하려면, 평등을 반환 할 때 해시 코드의 일반 계약이 동일하게되도록하는 방법은 무엇입니까?
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;
}
}
이 클래스에 대한 hashcode () 함수를 어떻게 작성합니까? 그리고 나는 여기에서 상수로 돌아 오는 사소한 사건을 피하고 싶습니다.
public int hashCode() {
return 1;
}
해결책
사소한 해시 코드가 존재한다고 생각하지 않습니다. 또한, 당신의 equals()
일반 계약을 위반합니다 API에 언급되었습니다 --- 그것은이다 전이적이지 않습니다:
(1,2)
동등합니다 (1,3)
(4,3)
동등합니다 (1,3)
하지만 (4,3)
~이다 동일하지 않습니다 에게 (1,2)
.
주장하다: 해시 코드는 사소한 상수 기능이어야합니다.
증거: 허락하다 (a,b)
그리고 (c,d)
별개의 해시 코드가있는 두 개의 물체가 되십시오 h(a,b) ≠ h(c,d)
. 대상을 고려하십시오 (a,d)
. OP의 정의에 의해 (a,d)
와 동등하다 (a,b)
, 그리고 (a,d)
와 동등하다 (c,d)
. 그것은 다음에 따른다 해시 코드 계약 저것 h(a,d) = h(a,b) = h(c,d)
; 모순.
다른 팁
Ok, 시나리오에서 API 요구 사항을 1 초 동안 무시하면 비 정식 해시 기능이 없습니다.
다른 값을 가진 해시 장애가 있다고 상상해보십시오.
(a, b), (a, c), b! = c, 그 다음 해시 (a, b)! = hash (a, c), eventhough (a, b) = (a, c).
유사하게, (b, a) 및 (c, a)는 동일한 해시 코드를 방출해야합니다.
해시 기능 h라고 부르겠습니다. 우리는 찾는다:
h (x, y) = h (x, w) = h (v, w) x, y, v, w.
따라서 원하는 것을 수행하는 유일한 해시 기능은 일정합니다.
나는 Zach의 옳다고 확신합니다.이를 수행 할 사소한 해시 코드는 없습니다.
의사 방지 :
x = (id1, name1)과 y = (id2, name2)의 두 가지 비평 값을 고려하십시오.
이제 z = (id2, name1)를 고려하십시오. 이것은 x와 y와 동일하므로 x 및 y와 동일한 해시 코드를 가져야합니다. 따라서 x와 y는 동일한 해시 코드를 가져야합니다. 모두 값은 동일한 해시 코드를 가져야합니다.
이상한 상황에 처한 이유가 있습니다. X.equals (z) 및 Z.equals (y) 사실 ~해야 한다 x.equals (y)를 의미하지만 그렇지 않습니다. 평등의 정의는 평등의 정상적인 계약에 적합하지 않습니다.
나는 당신이 할 수 없다고 생각합니다. 그 이유는 당신의 것입니다 equals()
방법은 전이적이지 않습니다.
전이성은 3 개의 비 널 X, y, Z, if에 대한 수단 x.equals(y)
, y.equals(z)
, 그 다음에 x.equals(z)
. 당신의 예에서는 객체입니다 x={id: 1, name: "ha"}
, y={id: 1, name: "foo"}
, z={id: 2, name: "bar"}
이 속성이 있습니다 (x.equals(y) and y.equals(z))
. 하지만, x.equals(z)
~이다 false
. 모든 equals()
방법에는이 속성이 있어야합니다. Java API 문서를 참조하십시오.
해싱 함수로 돌아 가기 : 모든 함수는 f(x)==f(y)
. 즉, 함수 값의 비교에 관심이 있고 TRUE를 반환하기를 원한다면 x==y
(아마도 다른 경우), 당신은 전이 관계를 받게 될 것입니다. 즉, 객체의 동등성을 적어도 전이적으로 폐쇄하는 것을 고려해야합니다. 귀하의 경우, 전이 폐쇄는 사소한 관계입니다 (모든 것은 무엇이든 동일합니다). 즉, 다른 객체를 어떤 함수로 구별 할 수 없습니다.
ID가 동일하거나 이름이 동일 할 때와 같이 의도적으로 평등을 정의 했습니까?
당신이 "및"그리고 "당신의 해시 코드는 동일하거나 적은 (동일하게 사용하지 않는 필드를 사용하지 않는) 필드를 사용하여 계산해야한다면, 당신은 equals ()에 의해 계산되어야합니다 ().
당신이 "또는"당신이 당신의 r hashgcode는 실제로 의미가없는 해시 코드 계산에 ID 또는 이름을 포함하지 않아야한다는 것을 의미하는 경우.
편집 : 질문을주의 깊게 읽지 않았습니다.
--
커먼즈 라인 항아리를 사용하겠습니다.
XOR 회원 해시 코드가 작동해야합니다. hashcode ()와 equals ()를 올바르게 구현해야합니다.
그러나 해시 코드를 보호하지 않으면 코드가 잘못 될 수 있습니다. 해시되면 변경해서는 안됩니다. 일어나지 않아야합니다.
public hashCode(){
return new AssertionError();
}
또는
public class MyClass {
final int id;
final String name;
// constructor
}
또는
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();
}
}
질문을 다시 읽은 후.
다른 필드가 업데이트 될 때 다른 필드를 자동으로 완성 할 수 있습니다.
--
편집 : 내 코드는 내 영어보다 더 잘 말할 수 있습니다.
void setName(String value){
this.id=Lookup.IDbyName(value);
}
void setID(String value){
this.name=Lookup.NamebyId(value);
}
편집 2 :
ID & 이름을 모두 설정하지 않으면 항상 사실을 반환하기 때문에 질문의 코드가 잘못 될 수 있습니다.
부분적으로 부분적으로 수행하는 메소드를 실제로 원한다면 "partialequals ()"라는 이름의 API를 작성하십시오.
가장 간단한 경로는 각 개별 필드의 해시 코드를 xor하는 것입니다. 이것은 일부 상황에서 약간의 추악함이 있습니다 (예 : X, Y 좌표는 X와 Y를 뒤집을 때 동일한 해시를 가질 수있는 잠재적으로 열악한 상황을 유발합니다). 효율성을 위해 필요한 경우 충돌을 줄이기 위해 필요에 따라 조정하십시오.
이건 어때
public override int GetHashCode()
{
return (id.ToString() + name.ToString()).GetHashCode();
}
기능은 Allways Allways "유효한"해시를 반환해야합니다 ...
편집 : 방금 사용한다는 것을 알았습니다.
어때
public override int GetHashCode()
{
return id.GetHashCode() ^ name.GetHashCode();
}