hashcode () 및 equals () 메소드는 Java의 완전히 객체 지향적 인 주요 특징이라고 할 수 있습니다. 그것은 우리의 프로그래밍을 촉진하고 많은 위험을 초래합니다. 이 기사에서는이 두 가지 방법을 올바르게 이해하고 사용하는 방법에 대해 논의 할 것입니다.
equals () 메소드를 다시 작성하기로 결정한 경우, 그렇게함으로써 발생하는 위험에 대해 명확해야하고 강력한 equals () 메소드를 작성할 수 있어야합니다. 주목해야 할 것은 ()를 다시 작성한 후 ()을 다시 작성해야한다는 것입니다. 구체적인 이유는 나중에 설명 될 것입니다.
먼저 Javase 7 사양의 equals () 메소드에 대한 설명을 살펴 보겠습니다.
∎ 반사적입니다 : 널 비 참조 값 x, x.equals(x) true 반환해야합니다.
・ 대칭입니다. 널 비 널 참조 값 x 와 y, x.equals(y) y.equals(x) 가 true 반환하는 경우에만 true 반환해야합니다.
・ 전이적입니다. x.equals(y) 가 true를 반환하면 y.equals(z) true 하면 널없는 참조 값 x, y, x.equals(z) true.
・ 일관성 : 널 비 참조 값 x 와 y , x.equals(y) 의 다중 호출은 객체에 대한 평등 비교에 사용 된 정보가 수정되지 않은 경우 false 되게 true 또는 일관되게 반환합니다.
null 비 참조 값 x, x.equals(null) 가 false 반환해야합니다.
이 구절은 개별 수학에서 많은 수비학을 사용합니다. 간단한 설명을하겠습니다.
1. 반사성 : a.equals (a)는 참으로 돌아와야합니다.
2. 대칭 : a.equals (b)가 true를 반환하면 B.equals (a)도 참으로 반환해야합니다.
3. 전송 : a.equals (b)가 참이고 b.equals (c)가 참이면 A.equals (c)도 참이어야합니다. 무뚝뚝하게 말하면 a = b, b = c, a = C
4. 일관성 : A와 B 객체의 상태가 변하지 않는 한, aequals (b)는 항상 참으로 반환해야합니다.
5. A.Equals (null)가 거짓을 반환합니다.
나는 수학에 전문적이지 않은 사람들이 위의 것들을 부르지 않을 것이라고 믿는다. 실제 응용 프로그램에서는 특정 단계에 따라 equals () 메소드 만 다시 작성하면됩니다. 설명의 편의를 위해 먼저 프로그래머 클래스 (코더)를 정의합니다.
클래스 코더 {개인 문자열 이름; 사적인 int 연령; // getters and setters}우리가 원하는 것은 두 프로그래머 객체의 이름과 연령이 동일하다면이 두 프로그래머가 동일하다고 생각합니다. 현재 equals () 메소드를 다시 작성해야합니다. 기본값 ()은 실제로 두 참조가 동일한 객체의 고유를 가리키는 지 여부를 결정하기 때문에 ==와 같습니다. 다시 쓰면 다음 세 단계를 따르십시오.
1. 그것이 자신과 동등한 지 여부를 결정하십시오.
if (Other == this)는 true를 반환합니다.
2. 연산자 인스턴스를 사용하여 다른 사람이 유형 코더의 객체인지 확인하십시오.
if (! (다른 인스턴스 코더)) false를 반환합니다.
3. 코더 클래스에서 사용자 정의하는 데이터 도메인, 이름 및 연령을 비교하면 놓치지 않아야합니다.
코더 o = (코더) 기타; return o.name.equals (이름) && O.age == Age;
이것을보고, 누군가가 물어볼 수 있습니다. 3 단계에는 캐스트가 있습니다. 누군가가 정수 클래스의 대상을이 평등에 전달하면 ClassCastException을 던질 것인가? 이 걱정은 실제로 중복됩니다. 우리는 두 번째 단계에서 인스턴스의 판단을 내렸기 때문에 다른 사람이 비 코더 객체이거나 다른 사람도 null이라면이 단계에서 False가 직접 반환되므로 후속 코드는 실행할 기회를 얻지 못할 것입니다.
위의 세 단계는 또한 <effect Java>에서 권장되는 단계이며 기본적으로 실수가 없도록 할 수 있습니다.
Javase 7 사양에서
"이 메소드 (Equals)가 재정의 될 때마다 해시 코드 메소드를 재정의 할 때마다 해시 코드 방법에 대한 일반 계약을 유지하기 위해 일반적으로 해시 코드 메소드를 재정의해야합니다.
equals () 메소드를 다시 작성하면 hashcode () 메소드를 다시 작성해야합니다. 우리는 대학 컴퓨터 데이터 구조 과정에서 해시 테이블을 배웠습니다. hashcode () 메소드는 해시 테이블을 사용합니다.
Hashmap 및 Hashset과 같은 해시와 같은 해시로 시작하는 컬렉션 클래스를 사용하면 Hashcode ()가 암시 적으로 호출되어 해시 매핑 관계를 만듭니다. 나중에 이것을 설명하겠습니다. 여기서 우리는 먼저 hashcode () 메소드의 쓰기에 중점을 둘 것입니다.
<효과적인 Java>는 해시 충돌을 가장 많이 피할 수있는 작문 방법을 제공하지만 개인적으로 일반적인 응용 프로그램에 많은 문제를 일으킬 필요는 없다고 생각합니다. 응용 프로그램에 수만 또는 수백만 개의 물체를 저장 해야하는 경우 책에 주어진 방법을 엄격히 따라야합니다. 중소형 응용 프로그램을 작성하는 경우 다음 원칙이 충분합니다.
코더 객체의 모든 멤버를 해시 코드에 반영 할 수 있도록해야합니다.
이 예를 들어, 우리는 이것을 쓸 수 있습니다.
@override public int hashcode () {int result = 17; 결과 = 결과 * 31 + name.hashcode (); 결과 = 결과 * 31 + 연령; 반환 결과; }int result = 17에서도 20, 50 등으로 변경할 수 있습니다. 문서를 확인하고 다음을 알고 있습니다.
"이 문자열의 해시 코드를 반환합니다. 문자열 객체의 해시 코드는 다음과 같이 계산됩니다.
S [0]*31^(N-1) + S [1]*31^(N-2) + ... + S [N-1]
int 산술 사용, 여기서 s [i]는 문자열의 문자, n은 문자열의 길이이고 ^는 지수를 나타냅니다. (빈 문자열의 해시 값은 0입니다.) "
각 문자의 ASCII 코드를 전원 n -1로 계산 한 다음 추가하십시오. 해시 코드 구현에서 태양이 매우 엄격하다는 것을 알 수 있습니다. 이것은 두 개의 다른 문자열에서 동일한 해시 코드를 가장 많이 피할 수 있습니다.
버킷의 개념은 Oracle의 해시 테이블 구현에서 참조됩니다. 아래 그림과 같이 :
위 그림에서 볼 수 있듯이 버킷이있는 해시 테이블은 해시 테이블과 링크 된 목록의 조합과 거의 같습니다. 즉, 링크 된 목록이 각 버킷에 매달려 있으며 링크 된 목록의 각 노드는 객체를 저장하는 데 사용됩니다. Java는 hashcode () 메소드를 사용하여 객체가 위치 해야하는 버킷을 결정한 다음 해당 링크 된 목록에서 검색합니다. 이상적으로는 hashcode () 메소드가 충분히 강력하게 쓰여지면 각 버킷에는 하나의 노드 만 가지면 검색 작업의 일정한 수준 시간 복잡성을 달성합니다. 즉, 객체에 어떤 메모리를 배치하든 상관없이 처음부터 끝까지 횡단 및 검색없이 hashcode ()을 통해 영역을 즉시 찾을 수 있습니다. 이것은 또한 해시 테이블의 주요 응용 프로그램입니다.
좋다:
해시 세트의 Put (Object O) 메소드를 호출하면 먼저 O.HASHCODE ()의 리턴 값에 따라 해당 버킷에서 위치합니다. 버킷에 노드가 없으면 여기에 O를 넣으십시오. 이미 노드가있는 경우 링크 된 목록의 끝까지 O를 매달아야합니다. 마찬가지로, 호출 (개체 O)에 호출 할 때 Java는 해시 코드 ()의 리턴 값을 통해 해당 버킷을 찾은 다음 해당 링크 목록의 노드에서 equals () 메소드를 호출하여 노드의 개체가 원하는 객체인지 여부를 결정합니다.
예제를 사용 하여이 과정을 경험해 봅시다.
먼저 두 개의 새로운 코더 객체를 만들어 봅시다.
코더 C1 = 새로운 코더 ( "브루스", 10); 코더 C2 = 새로운 코더 ( "브루스", 10);
hashcode () 메소드를 다시 작성하지 않고 코더의 equals () 메소드를 다시 작성했다고 가정합니다.
@override public boolean equals (Object Other) {System.out.println ( "Equals 메소드 호출!"); if (Other == this)는 true를 반환합니다. if (! (다른 인스턴스 코더)) false를 반환합니다. 코더 o = (코더) 기타; return o.name.equals (이름) && O.age == Age; }그런 다음 해시 세트를 구성하고 C1 객체를 세트에 넣습니다.
<coder> set = new Hashset <coder> (); set.add (c1);
다시 실행 :
System.out.println (set.crintains (c2));
포함 (C2) 메소드가 True를 반환 할 것으로 예상되지만 실제로는 False를 반환합니다.
C1과 C2의 이름과 나이는 동일합니다. C1을 해시 세트에 넣은 후 (C2) COLLING을 호출하고 False를 반환하는 이유는 무엇입니까? 이것은 문제를 일으키는 hashcode ()입니다. Hashcode () 메소드를 다시 작성하지 않으므로 Hashset이 C2를 찾으면 다른 버킷에서 찾을 수 있습니다. 예를 들어, C1을 버킷 05에 넣으면 C2를 검색 할 때 버킷 06에서 검색되므로 물론 찾을 수 없습니다. 따라서 hashcode ()를 다시 쓰는 목적은 a.equals (b)가 true를 반환 할 때 a와 b의 hashcode ()가 동일한 값을 반환해야한다는 것입니다.
hashcode ()에게 매번 고정 번호 줄을 반환하도록 요청합니까?
누군가가 다음과 같이 다시 작성할 수 있습니다.
@override public int ashcode () {return 10; }이 경우 해시 맵, 해시 세트 및 기타 컬렉션 클래스는 "해시 의미"를 잃게됩니다. <EFFECT JAVA>의 말로, 해시 테이블은 링크 된 목록으로 변성됩니다. hashcode ()가 매번 같은 숫자를 반환하면 모든 객체가 동일한 버킷에 배치되며 검색 작업을 수행 할 때마다 링크 된 목록을 통과하여 해싱의 기능을 완전히 잃게됩니다. 따라서 좋은 아이디어로 강력한 해시 코드 ()를 제공하는 것이 좋습니다.
위의 내용은 Hashcode () 및 equals () 메소드를 다시 작성하는 것에 대한이 기사의 모든 자세한 소개입니다. 모든 사람에게 도움이되기를 바랍니다. 관심있는 친구는이 사이트의 다른 관련 주제를 계속 참조 할 수 있습니다. 단점이 있으면 메시지를 남겨 두십시오. 이 사이트를 지원해 주신 친구들에게 감사드립니다!