hashCode()
컬렉션에 가장 적합한 메서드 구현을 결정하려면 어떻게해야합니까 (메소드와 같은 것이 올바르게 재정의되었다고 가정)?
collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… ) 를 반환합니다.
hashCode()
컬렉션에 가장 적합한 메서드 구현을 결정하려면 어떻게해야합니까 (메소드와 같은 것이 올바르게 재정의되었다고 가정)?
collection.hashCode()
( hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/… ) 를 반환합니다.
답변:
최고의 구현? 사용법 패턴에 따라 다르므로 어려운 질문입니다.
거의 모든 경우에 합리적인 구현이 Josh Bloch 의 효과적인 Java 항목 8 (제 2 판)에서 제안되었습니다 . 가장 좋은 방법은 저자가 접근 방식이 좋은 이유를 설명하기 때문에 거기서 찾아 보는 것입니다.
a를 작성하고 0이 아닌 값을 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)
모든 긴 값처럼 리턴 값을 계산 하고 처리하십시오.hashCode()
메소드 의 결과를 사용 하거나 0이면 f == null
;해시 값 c
을 result
다음 과 결합하십시오 .
result = 37 * result + c
반환 result
이로 인해 대부분의 사용 상황에 적절한 해시 값이 배포됩니다.
dmeister가 권장하는 효과적인 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
는 custom이있는 경우 equals
이며, 이러한 라이브러리 메소드가 설계된 것입니다. 설명서와 관련하여 해당 동작이 명확합니다 equals
. 라이브러리 구현은 올바른 hashCode
구현 의 특성이 무엇인지 알지 못한다고 주장하지 않습니다. 이러한 라이브러리를 사용하면 재정의 되는 대부분의 경우에 적합한 구현을 쉽게 구현할 수 equals
있습니다.
java.util.Objects.hash(...)
구아바 com.google.common.base.Objects.hashCode(...)
방법 보다 JDK7 방법 을 선택했을 것 입니다. 나는 대부분의 사람들이 추가적인 의존성보다 표준 라이브러리를 선택할 것이라고 생각합니다.
hashCode()
배열에 대한 바로 그 것이다 java.lang.System.identityHashCode(...)
.
꽤 좋은 일을하는 Eclipse가 제공하는 기능을 사용하는 것이 좋으며 비즈니스 로직을 개발하는 데 노력과 에너지를 투입 할 수 있습니다.
이것은 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
여기 Github에서 좋은 참조가 있습니다 ...
@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 기사 :
- 대칭 : 두 참조 a와 b의 경우 b.equals (a) 인 경우에만 a.equals (b)
- 반사성 : null이 아닌 모든 참조의 경우 a.equals (a)
- 전이성 : aequals (b) 및 b.equals (c)이면 a.equals (c)
그런 다음 hashCode와의 관계가 동일한 기사의 연락처를 존중하는지 확인하십시오.
- hashCode ()와의 일관성 : 두 개의 동일한 객체는 동일한 hashCode () 값을 가져야합니다.
마지막으로 좋은 해시 함수는 이상적인 해시 함수 에 접근하기 위해 노력해야 합니다 .
about8.blogspot.com, 당신은 말했다
두 객체에 대해 equals ()가 true를 반환하면 hashCode ()는 동일한 값을 반환해야합니다. equals ()가 false를 반환하면 hashCode ()는 다른 값을 반환해야합니다
나는 당신에 동의 할 수 없습니다. 두 객체에 동일한 해시 코드가있는 경우 동일한 해시 코드를 의미 할 필요는 없습니다.
A가 B와 같으면 A.hashcode는 B.hascode와 같아야합니다.
그러나
A.hashcode가 B.hascode와 같다고해서 A가 B와 같아야한다는 의미는 아닙니다.
(A != B) and (A.hashcode() == B.hashcode())
해시 함수 충돌이라고합니다. 해시 함수의 공동 도메인은 항상 유한하기 때문에 도메인은 일반적으로 그렇지 않기 때문입니다. 코 도메인이 클수록 충돌 발생 빈도가 줄어 듭니다. 좋은 해시 함수는 특정 코 도메인 크기에서 가장 큰 가능성을 가지고 다른 객체에 대해 다른 해시를 반환해야합니다. 그래도 완전히 보장되지는 않습니다.
이클립스를 사용하면 다음을 생성 equals()
하고 hashCode()
사용할 수 있습니다 .
소스-> hashCode () 및 equals () 생성
이 함수 를 사용하면 동등성 및 해시 코드 계산에 사용할 필드 를 결정할 수 있으며 Eclipse는 해당 메소드를 생성합니다.
의 좋은 구현에있어 효과적인 자바 의 hashcode()
와 equals()
의 논리 아파치 코 몬즈 랭 . 체크 아웃 HashCodeBuilder 및 EqualsBuilder .
Objects
클래스는 Java7의 메소드 hash(Object ..args)
및 equals()
메소드를 제공합니다 . jdk 1.7 이상을 사용하는 모든 응용 프로그램에 권장됩니다
IdentityHashMap
). FWIW ID 기반 hashCode를 사용하고 모든 엔티티에 동일합니다.
다른 더 자세한 답변을 완성하기위한 간단한 참고 사항 (코드 측면에서) :
내가 질문을 고려하면 방법 - 할 - 내가 만들 -에 - 자바 A-해시 테이블 특히 jGuru FAQ를 , 내가 해시 코드를 판단 할 수있는에 따라 다른 기준이 있습니다 생각 :
질문을 올바르게 이해하면 사용자 정의 컬렉션 클래스 (즉, Collection 인터페이스에서 확장되는 새 클래스)가 있고 hashCode () 메서드를 구현하려고합니다.
컬렉션 클래스가 AbstractList를 확장한다면 걱정할 필요가 없습니다. 모든 객체를 반복하고 hashCodes ()를 함께 추가하여 작동하는 equals () 및 hashCode () 구현이 이미 있습니다.
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을 건너 뛰십시오.
foo
과 bar
동일 hashCode
합니다. 귀하의 toString
AFAIK는 컴파일되지 않습니다, 그것은 않을 경우, 그것은 비효율적 끔찍. 같은 뭔가가 109 * getFoo().hashCode() + 57 * getBar().hashCode()
빠르고, 간단하고 더 불필요한 충돌을 생산하지 않습니다.
Apache Commons EqualsBuilder 및 HashCodeBuilder 에서 리플렉션 메소드를 사용하십시오 .
Arrays.deepHashCode(...)
매개 변수로 제공된 배열을 올바르게 처리하기 때문에 작은 래퍼를 사용 합니다.
public static int hash(final Object... objects) {
return Arrays.deepHashCode(objects);
}
가능한 범위에 해시 값을 균등하게 분배하는 모든 해싱 방법이 적합합니다. (유효 자바를 참조 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 , 좋은 팁이있다) 해시 코드 구현을 위해 거기에 (항목 9 생각합니다 ...).
다음은 슈퍼 클래스 로직을 고려한 또 다른 JDK 1.7+ 접근 데모입니다. Object 클래스 hashCode () 계정, 순수한 JDK 종속성 및 추가 수동 작업이없는 것이 매우 편리합니다. 참고하시기 바랍니다 Objects.hash()
널 (null) 허용입니다.
나는 어떤 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의 배수는 2를 잃는 등). 홀수 승수를 사용할 수 있습니다. 작은 승수는 더 빠른 계산으로 이어질 수 있지만 (JIT는 시프트 및 덧셈을 사용할 수 있음), 현대 인텔 / 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")
. 심각한 결함입니다. 두 필드의 해시 코드를 평가 한 다음 선형 조합을 계산하는 것이 좋습니다 (프라임을 계수로 사용하는 것이 좋습니다).
foo
및 bar
불필요한 충돌도 발생합니다.
Objects.hashCode(collection)
완벽한 솔루션이어야합니다!