최선의 구현 방법을 결정하는 방법hashCode()
컬렉션에 대한 메서드 (equals 메서드가 올바르게 재정의되었다고 가정)?
최상의 구현? 그것은 사용 패턴에 의존하기 때문에 어려운 질문입니다.
거의 모든 경우에있어 합리적인조쉬 블로흐'에스효과적인 자바제 8 조 (제 2 판). 저자가 왜 접근법이 좋은지를 설명하기 때문에 가장 좋은 점은 그곳을 살펴 보는 것입니다.
만들기int result
그리고제로값.
에 대한모든 분야
f
테스트를 거쳤습니다.equals()
메소드, 해시 코드 계산c
으로:
boolean
:
계산하다(f ? 0 : 1)
;byte
,char
,short
또는int
: 계산하다(int)f
;long
: 계산하다(int)(f ^ (f >>> 32))
;float
: 계산하다Float.floatToIntBits(f)
;double
: 계산하다Double.doubleToLongBits(f)
모든 long 값처럼 반환 값을 처리합니다.hashCode()
메서드 또는 0 iff == null
;해시 값 결합c
와result
:
result = 37 * result + c
반환result
대부분의 사용 상황에서 해시 값을 올바르게 분배해야합니다.
dmeister가 권장하는 Effective Java 구현에 만족하면 자신 만의 라이브러리 호출 대신 라이브러리 호출을 사용할 수 있습니다.
@Override
public int hashCode() {
return Objects.hashCode(this.firstName, this.lastName);
}
이것은 Guava (com.google.common.base.Objects.hashCode
) 또는 Java 7의 표준 라이브러리 (java.util.Objects.hash
)하지만 같은 방식으로 작동합니다.
hashCode
당신이 관례가 있다면equals
, 그리고 그것은 정확하게 이러한 라이브러리 메소드가 설계된 것입니다. 문서는 자신의 행동과 관련하여 매우 분명합니다.equals
. 라이브러리 구현은 올바른 것의 특성을 알지 못하게한다고 주장하지 않습니다.hashCode
구현은 다음과 같습니다 -이 라이브러리는더 쉬운대부분의 경우에 이러한 준수 구현을 구현할 수 있습니다.equals
재정의됩니다. - bacarjava.util.Objects.hash(...)
구아바보다 방법com.google.common.base.Objects.hashCode(...)
방법. 나는 대부분의 사람들이 여분의 의존성을 넘어 표준 라이브러리를 선택할 것이라고 생각한다. - Malte SkoruppahashCode()
배열은java.lang.System.identityHashCode(...)
. - starikoff
이클립스가 제공하는 기능을 사용하는 것이 더 낫다. 비즈니스 로직을 개발하는 데 노력과 에너지를 투입 할 수있다.
이것은에 연결되어 있지만Android
문서화 (Wayback Machine)과내 자신의 Github 코드, 그것은 일반적으로 Java에서 작동합니다. 내 대답은dmeister 님의 답변읽기 쉽고 이해하기 쉬운 코드 만 있으면됩니다.
@Override
public int hashCode() {
// Start with a non-zero constant. Prime is preferred
int result = 17;
// Include a hash for each field.
// Primatives
result = 31 * result + (booleanField ? 1 : 0); // 1 bit » 32-bit
result = 31 * result + byteField; // 8 bits » 32-bit
result = 31 * result + charField; // 16 bits » 32-bit
result = 31 * result + shortField; // 16 bits » 32-bit
result = 31 * result + intField; // 32 bits » 32-bit
result = 31 * result + (int)(longField ^ (longField >>> 32)); // 64 bits » 32-bit
result = 31 * result + Float.floatToIntBits(floatField); // 32 bits » 32-bit
long doubleFieldBits = Double.doubleToLongBits(doubleField); // 64 bits (double) » 64-bit (long) » 32-bit (int)
result = 31 * result + (int)(doubleFieldBits ^ (doubleFieldBits >>> 32));
// Objects
result = 31 * result + Arrays.hashCode(arrayField); // var bits » 32-bit
result = 31 * result + referenceField.hashCode(); // var bits » 32-bit (non-nullable)
result = 31 * result + // var bits » 32-bit (nullable)
(nullableReferenceField == null
? 0
: nullableReferenceField.hashCode());
return result;
}
편집하다
일반적으로hashcode(...)
, 당신은 또한 오버라이드하고 싶다.equals(...)
. 이미 구현되거나 이미 구현 된 사람들에게는equals
여기에 좋은 참고 자료가있다.내 기둥에서...
@Override
public boolean equals(Object o) {
// Optimization (not required).
if (this == o) {
return true;
}
// Return false if the other object has the wrong type, interface, or is null.
if (!(o instanceof MyType)) {
return false;
}
MyType lhs = (MyType) o; // lhs means "left hand side"
// Primitive fields
return booleanField == lhs.booleanField
&& byteField == lhs.byteField
&& charField == lhs.charField
&& shortField == lhs.shortField
&& intField == lhs.intField
&& longField == lhs.longField
&& floatField == lhs.floatField
&& doubleField == lhs.doubleField
// Arrays
&& Arrays.equals(arrayField, lhs.arrayField)
// Objects
&& referenceField.equals(lhs.referenceField)
&& (nullableReferenceField == null
? lhs.nullableReferenceField == null
: nullableReferenceField.equals(lhs.nullableReferenceField));
}
먼저 equals가 올바르게 구현되었는지 확인하십시오. 에서IBM DeveloperWorks 기사:
- 대칭 (symmetric) : 두 개의 참조 a와 b에 대해, a.equals (b) b.equals (a)
- 반사성 : null 이외의 모든 참조의 경우, a.equals (a)
- 전이성 : a.equals (b)와 b.equals (c)가있는 경우, a.equals (c)
그런 다음 hashCode와의 관계가 (동일한 아티클의) 연락처를 존중하는지 확인하십시오.
- hashCode ()와의 일관성 : 두 개의 동일한 객체는 동일한 hashCode () 값을 가져야합니다.
마지막으로 좋은 해시 함수는이상적인 해시 함수.
about8.blogspot.com, 당신이 말했습니다.
equals ()가 2 개의 객체에 대해서 true를 돌려주는 경우, hashCode ()는 같은 값을 돌려 줄 필요가 있습니다. equals ()가 false를 반환하면 hashCode ()는 다른 값을 반환해야합니다.
나는 너에게 동의 할 수 없다. 2 개의 오브젝트가 같은 해시 코드를 가지는 경우, 그것들이 동일하다는 것을 의미 할 필요는 없다.
A가 B와 같으면 A.hashcode는 B.hascode와 같아야합니다.
그러나
A.hashcode가 B.hascode와 같으면 A가 반드시 B와 같아야 함을 의미하지는 않습니다.
(A != B) and (A.hashcode() == B.hashcode())
, 해시 함수 충돌이라고하는 것입니다. 해시 함수의 코드 도메인은 항상 유한하지만 도메인은 일반적으로 그렇지 않기 때문에 결과가 다릅니다. codomain이 클수록 충돌이 적게 발생합니다. 좋은 해시 함수는 특정 코드 영역 크기가 주어질 때 최대 가능성을 가진 다른 객체에 대해 다른 해시를 반환해야합니다. 그것은 거의 완벽하게 보장 할 수는 없습니다. - Krzysztof Jabłoński
이클립스를 사용한다면, 생성 할 수있다.equals()
과hashCode()
사용 :
Source -> hashCode () 및 equals ()를 생성합니다.
이 기능을 사용하면어느 필드당신은 평등과 해시 코드 계산을 위해 사용하고, Eclipse는 상응하는 메소드를 생성한다.
좋은 구현 방법이 있습니다.효과적인 자바'에스hashcode()
과equals()
논리아파치 커먼즈 랭. 점검HashCodeBuilder과EqualsBuilder.
Objects
수업 제공hash(Object ..args)
&equals()
Java7의 메소드. JDK 1.7 이상을 사용하는 모든 응용 프로그램에 권장됩니다. - DiabloIdentityHashMap
). FWIW 나는 모든 엔티티에 대해 id 기반 hashCode와 equals를 사용합니다. - maaartinus
다른보다 자세한 답변을 완성하기위한 간단한 참고 사항 (코드의 용어로) :
질문을 생각하면해시 테이블 - 인 - 자바 (How-do-i-create-a-hash-table-in-java)특히jGuru FAQ 항목, 해시 코드를 판단 할 수있는 다른 기준은 다음과 같습니다.
귀하의 질문을 올바르게 이해하면 맞춤 컬렉션 클래스 (Collection 인터페이스에서 확장되는 새로운 클래스)가 있고 hashCode () 메소드를 구현하고 싶습니다.
Collection 클래스가 AbstractList를 확장한다면, 걱정할 필요가 없습니다. equals ()와 hashCode ()의 구현은 이미 모든 객체를 반복하고 해시 코드 (hashCodes)를 함께 추가하여 작동합니다.
public int hashCode() {
int hashCode = 1;
Iterator i = iterator();
while (i.hasNext()) {
Object obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
이제 원하는 클래스가 특정 클래스의 해시 코드를 계산하는 가장 좋은 방법 인 경우 일반적으로 ^ (비트 전용 또는 연산자)를 사용하여 equals 메서드에서 사용하는 모든 필드를 처리합니다.
public int hashCode(){
return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}
@ about8 : 꽤 심각한 버그가 있습니다.
Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");
같은 해시 코드
너는 아마 뭔가를 원할거야.
public int hashCode() {
return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();
(요즘 Java의 int에서 hashCode를 직접 가져올 수 있습니까? 필자는 자동 채우기를 수행한다고 생각합니다. 그렇다면 toString을 건너 뜁니다.)
컬렉션에 대해 특별히 질문 했으므로 다른 답변에서 아직 언급하지 않은 부분을 추가하고 싶습니다. HashMap은 키가 컬렉션에 추가되면 해시 코드가 변경 될 것으로 기대하지 않습니다. 모든 목적을 이길 ...
Apache Commons에서 반사 방법 사용EqualsBuilder과HashCodeBuilder.
가능한 범위에서 해시 값을 균등하게 분배하는 해싱 방법은 좋은 구현입니다. 효과적인 자바보기 (http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=en&sa=X&oi=book_result&resnum=1&ct=result), 좋은 팁 거기에 hashcode 구현 (item 9 i think ...).
나는 유틸리티 메소드를 사용하는 것을 선호한다.Object 컬렉션의 Google Collections lib내 코드를 깨끗하게 유지하는 데 도움이됩니다. 매우 자주equals
과hashcode
메소드는 IDE의 템플릿에서 만들어 지므로 읽기에는 깨끗하지 않습니다.
나는 작은 래퍼를 사용한다.Arrays.deepHashCode(...)
매개 변수로 제공된 배열을 올바르게 처리하기 때문에
public static int hash(final Object... objects) {
return Arrays.deepHashCode(objects);
}
수퍼 클래스 로직이 설명 된 다른 JDK 1.7+ 접근 데모가 있습니다. Object 클래스 hashCode (), 순수한 JDK 종속성 및 추가 수동 작업이 없다는 것을 알았습니다. 주의 사항Objects.hash()
무효입니다.
나는 어떤 것도 포함하지 않았다.equals()
현실이지만 당신은 물론 그것을 필요로 할 것입니다.
import java.util.Objects;
public class Demo {
public static class A {
private final String param1;
public A(final String param1) {
this.param1 = param1;
}
@Override
public int hashCode() {
return Objects.hash(
super.hashCode(),
this.param1);
}
}
public static class B extends A {
private final String param2;
private final String param3;
public B(
final String param1,
final String param2,
final String param3) {
super(param1);
this.param2 = param2;
this.param3 = param3;
}
@Override
public final int hashCode() {
return Objects.hash(
super.hashCode(),
this.param2,
this.param3);
}
}
public static void main(String [] args) {
A a = new A("A");
B b = new B("A", "B", "C");
System.out.println("A: " + a.hashCode());
System.out.println("B: " + b.hashCode());
}
}
표준 구현은 약하고 불필요한 충돌로 연결됩니다. 상상해보십시오.
class ListPair {
List<Integer> first;
List<Integer> second;
ListPair(List<Integer> first, List<Integer> second) {
this.first = first;
this.second = second;
}
public int hashCode() {
return Objects.hashCode(first, second);
}
...
}
지금,
new ListPair(List.of(a), List.of(b, c))
과
new ListPair(List.of(b), List.of(a, c))
같아.hashCode
, 즉31*(a+b) + c
에 사용 된 승수로List.hashCode
여기에서 재사용된다. 분명히 충돌은 피할 수 없지만 불필요한 충돌을 일으키는 것은 단지 불필요한 것입니다.
사용법에 대해 현명한 것은 없습니다.31
. 승수는 정보 손실을 피하기 위해 홀수 여야합니다 (짝수 배수는 적어도 최하위 비트를 잃고 4의 배수는 두 개를 잃습니다 등). 임의의 승수를 사용할 수 있습니다. 작은 곱셈기로 인해 더 빠른 계산이 가능합니다 (JIT는 쉬프트 및 덧셈을 사용할 수 있습니다). 그러나 곱셈은 현대 Intel / AMD에서 단지 3 사이클의 대기 시간을 갖기 때문에 이것은 중요하지 않습니다. 작은 승수로 인해 작은 입력에 대해 더 많은 충돌이 발생하기 때문에 때때로 문제가 될 수 있습니다.
반지름 Z / (2 ** 32)에서 소수는 의미가 없으므로 소수를 사용하는 것은 의미가 없습니다.
그래서 무작위로 선택된 큰 홀수를 사용하는 것이 좋습니다 (소수를 자유롭게 사용하십시오). i86 / amd64 CPU는 단일 부호있는 바이트에 피연산자를 맞추기 위해 더 짧은 명령어를 사용할 수 있기 때문에 109와 같은 곱셈기에 약간의 속도 이점이 있습니다. 충돌을 최소화하기 위해 0x58a54cf5와 같은 것을 가져옵니다.
다른 장소에서 다른 승수를 사용하는 것이 도움이되지만, 추가 작업을 정당화하기에는 충분하지 않을 수 있습니다.
해시 값을 결합 할 때, 나는 보통 boost C ++ 라이브러리에서 사용되는 결합 방법을 사용합니다 :
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
이는 균등 분배를 보장하는 훌륭한 역할을합니다. 이 수식이 어떻게 작동하는지에 대한 설명은 StackOverflow 게시물을 참조하십시오.boost :: hash_combine의 매직 넘버
다른 해시 함수에 대한 좋은 토론은 다음과 같습니다.http://burtleburtle.net/bob/hash/doobs.html
간단한 클래스의 경우, equals () 구현에 의해 체크되는 클래스 필드에 근거 해 hashCode ()를 구현하는 것이 가장 쉬운 경우가 있습니다.
public class Zam {
private String foo;
private String bar;
private String somethingElse;
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Zam otherObj = (Zam)obj;
if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getFoo() + getBar()).hashCode();
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
가장 중요한 것은 hashCode () 및 equals ()를 일관되게 유지하는 것입니다. equals ()가 두 객체에 대해 true를 반환하면 hashCode ()가 동일한 값을 반환해야합니다. equals ()가 false를 반환하면 hashCode ()는 다른 값을 반환해야합니다.
("abc"+""=="ab"+"c"=="a"+"bc"==""+"abc")
. 그것은 심각한 결함입니다. 두 필드에 대한 해시 코드를 평가 한 다음 이들의 선형 조합을 계산하는 것이 더 좋습니다 (바람직하게는 소수를 계수로 사용). - Krzysztof Jabłońskifoo
과bar
불필요한 충돌을 일으킨다. - maaartinus
Objects.hashCode(collection)
완벽한 솔루션이어야합니다! - Diablocollection.hashCode()
(jpg) - cbreezier