hashcode() 메서드를 배우려면 다음과 같은 측면을 단계별로 파악해야 합니다.
1. hashcode()의 유래
hashcode는 jdk 기반입니다. 객체에서 주소, 문자열 또는 숫자로부터 계산된 int 유형 값은 어떻게 계산됩니까? 정답은 해시테이블이다. 해시 테이블은 키 값을 기반으로 직접 접근하는 데이터 구조이다. 이것은 너무 공식적인 것일 수 있습니다. 해시코드는 함수를 통해 매핑되는 값입니다. 이 함수를 함수에 전달하면 값이 반환됩니다. 해시 함수로 얻은 값은 해시 값, 즉 value=f(key)입니다. 따라서 해시코드를 이해한 후에는 hashcode() 메서드가 무엇에 사용되는지 알 수 있습니다. hashcode() 메서드는 hashcode 메서드를 계산하고 반환하는 것입니다.
2. hashcode()의 역할
해시 테이블의 가장 큰 장점은 검색 효율성을 높일 수 있다는 점입니다. API를 보면 hashcode()는 Object의 메소드라는 것을 알 수 있습니다. 또한 Java에서는 개선된 특성에 따라 모든 클래스의 객체가 hashcode() 메소드를 호출할 수 있다고도 할 수 있습니다. 검색 효율성을 높이기 위해 주로 해시 기반 컬렉션과 제대로 작동하기 위해 이러한 해시 컬렉션에는 HashSet, HashMap 및 HashTable이 포함됩니다. 세트에 중복된 요소가 존재할 수 없는데, 새 요소를 추가해야 할 때 해당 요소가 이미 존재하는지 어떻게 판단할 수 있나요? 아마도 대부분의 사람들은 하나씩 비교하기 위해 equals 메소드를 호출하는 것을 생각할 것이다. 하지만 컬렉션에 이미 10,000개 이상의 데이터가 있고, 이를 동등 메소드를 사용하여 하나씩 비교한다면 효율성이 문제가 될 것입니다. 이때 hashCode 메소드의 역할이 컬렉션에 추가되면 해당 객체의 hashCode 메소드가 먼저 호출되어 해당 해시코드 값을 가져옵니다. 실제로 HashMap의 특정 구현에서는 객체의 해시코드 값이 테이블에 없으면 비교 없이 바로 저장할 수 있습니다. 새 요소가 동일하면 비교되지 않습니다. 저장하면 다른 주소가 동일하지 않으면 해싱되므로 충돌 해결의 문제가 있으므로 실제 호출 횟수는 동일합니다. 방법이 많이 줄어들었습니다. 다음 코드는 java.util.HashMap의 put 메소드의 특정 구현입니다.
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
put 메소드는 HashMap에 새 요소를 추가하는 데 사용됩니다. hashCode 메소드를 호출하여 요소의 hashCode 값을 가져온 다음 hashCode 값이 테이블에 존재하는지 확인하고, 존재하는 경우에는 equals 메소드를 호출하여 업데이트하십시오. 값을 반환하고, 그렇지 않으면 HashMap에 새 요소를 추가합니다. 여기에서 보면 hashCode 메소드는 equals 메소드 호출 횟수를 줄여 프로그램 효율성을 높이기 위해 존재한다는 것을 알 수 있다.
3.hashcode() 메소드와 equals() 메소드
여기서 주목해야 할 몇 가지 문제가 있습니다. 객체가 동일한지 확인하려면 hashcode() 메서드를 사용할 수 있습니다. 대답은 '아니오'입니다. equals() 메서드를 사용해야 합니다. 서로 다른 두 객체는 동일한 해시코드를 가질 수 있지만, 해시코드가 다른 두 객체는 서로 달라야 합니다.
주의해야 할 또 다른 문제: 클래스를 설계할 때 String 클래스와 같은 equals 메서드를 재정의해야 하지만, equals 메서드를 다시 작성할 때는 hashCode 메서드도 재정의해야 한다는 점에 유의하세요. . equals 메소드만 재정의하는 예를 살펴보겠습니다.
import java.util.HashMap; import java.util.HashSet; import java.util.Set; class People{ private String name; private int age; public People(String name,int age) { this.name = name; this.age = age; } public void setAge(int age){ this.age = age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return this.name.equals(((People)obj).name) && this.age== ((People)obj).age; } } public class Main { public static void main(String[] args) { People p1 = new People("Jack", 12); System.out.println(p1.hashCode()); HashMap<People, Integer> hashMap = new HashMap<People, Integer>(); hashMap.put(p1, 1); System.out.println(hashMap.get(new People("Jack", 12))); } }
두 People 개체의 이름과 나이가 같으면 같은 사람으로 간주됩니다. 이 코드의 원래 의도는 이 코드의 출력 결과가 "1"이지만 실제로는 "null"을 출력하는 것입니다.
equals 메서드를 재정의하면 이름과 나이가 같은 두 개체가 논리적으로 동일한 개체로 판단되지만(String 클래스와 유사) 기본적으로 hashCode 메서드는 개체 Address를 저장한다는 점을 알아야 합니다. 매핑. 위 코드의 출력이 "null"이라는 것은 놀라운 일이 아닙니다. 이유는 매우 간단합니다. 이 문장에서 p1과 System.out.println(hashMap.get(new People("Jack", 12))); new People("Jack", 12)이 가리키는 개체는 두 개의 개체를 생성합니다. . , 저장 주소는 달라야 합니다. 따라서 해시맵이 get 연산을 수행하면 획득한 해시cdoe 값이 다르기 때문에 바로 null이 반환된다. 위 코드의 출력 결과를 "1"로 하려면 매우 간단합니다. equals 메서드와 hashCode 메서드가 항상 논리적으로 일치하도록 hashCode 메서드를 다시 작성하기만 하면 됩니다.
import java.util.HashMap; import java.util.HashSet; import java.util.Set; class People{ private String name; private int age; public People(String name,int age) { this.name = name; this.age = age; } public void setAge(int age){ this.age = age; } @Override public int hashCode() { // TODO Auto-generated method stub return name.hashCode()*37+age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return this.name.equals(((People)obj).name) && this.age== ((People)obj).age; } } public class Main { public static void main(String[] args) { People p1 = new People("Jack", 12); System.out.println(p1.hashCode()); HashMap<People, Integer> hashMap = new HashMap<People, Integer>(); hashMap.put(p1, 1); System.out.println(hashMap.get(new People("Jack", 12))); } }
다음은 Effective Java 책에서 인용한 내용입니다.
프로그램 실행 중, equals 메소드의 비교 연산에 사용된 정보가 수정되지 않는 한 , 동일한 객체를 여러 번 호출하면 hashCode 메서드는 일관되게 동일한 정수를 반환해야 합니다.
equals 메소드에 따라 두 객체가 동일한 경우 두 객체의 hashCode 메소드를 호출하면 동일한 정수 결과가 반환되어야 합니다.
equals 메소드에 따라 두 객체가 동일하지 않은 경우 hashCode 메소드가 반드시 다른 정수를 반환할 필요는 없습니다.
두 번째, 세 번째 항목은 이해하기 쉽지만 첫 번째 항목은 무시되는 경우가 많습니다. "Java 프로그래밍 생각" 책의 P495페이지 첫 번째 항목과 유사한 단락도 있습니다.
"hashCode()를 디자인할 때 가장 중요한 요소는 동일한 객체에 대해 hashCode()가 호출될 때마다 동일한 값을 생성해야 한다는 것입니다. put()을 사용하여 객체를 HashMap에 추가하면 hashCdoe가 생성됩니다. 값이 생성되지만 get()을 사용하면 다른 hashCode 값이 생성되므로 객체를 얻을 수 없습니다. 따라서 hashCode 메서드가 객체의 휘발성 데이터에 의존하는 경우 이 데이터가 변경될 수 있으므로 주의해야 합니다. , hashCode() 메서드는 다른 해시 코드를 생성합니다."