Member의 경우 회원의 id가 같으면 논리적으로 같은 회원으로 표현할 수 있음
따라서 회원 id를 기반으로 동등성을 비교하도록 equals를 재정의해야 함
여기에 hashCode()도 같은 원리가 적용됨
id가 같으면 논리적으로 같은 회원으로 표현할 수 있음id를 기반으로 해시 코드를 생성해야 함
hashCode()를 사용해서 모든 데이터 타입을 저장할 수 있는 MyHashSetV2
- 자바의
hashCode()를 사용하면 타입과 관계없이 해시 코드를 편리하게 구현할 수 있음
package collection.set;
import java.util.Arrays;
import java.util.LinkedList;
public class MyHashSetV2 {
static final int DEFAULT_INITIAL_CAPACITY = 16;
private LinkedList<Object>[] buckets;
private int size = 0;
private int capacity = DEFAULT_INITIAL_CAPACITY;
public MyHashSetV2() {
initBuckets();
}
public MyHashSetV2(int capacity) {
this.capacity = capacity;
initBuckets();
}
private void initBuckets() {
buckets = new LinkedList[capacity];
for (int i = 0; i < capacity; i++) {
buckets[i] = new LinkedList<>();
}
}
public boolean add(Object value) {
int hashIndex = hashIndex(value);
LinkedList<Object> bucket = buckets[hashIndex];
if (bucket.contains(value)) {
return false;
}
bucket.add(value);
size++;
return true;
}
public boolean contains(Object searchValue) {
int hashIndex = hashIndex(searchValue);
LinkedList<Object> bucket = buckets[hashIndex];
return bucket.contains(searchValue);
}
public boolean remove(Object value) {
int hashIndex = hashIndex(value);
LinkedList<Object> bucket = buckets[hashIndex];
boolean result = bucket.remove(value);
if (result) {
size--;
return true;
}
else {
return false;
}
}
private int hashIndex(Object value) {
//hashCode의 결과로 음수가 나올 수 있다. abs()를 사용해서 마이너스를 제거한다.
return Math.abs(value.hashCode()) % capacity;
}
public int getSize() {
return size;
}
@Override
public String toString() {
return "MyHashSetV2{" +
"buckets=" + Arrays.toString(buckets) +
", size=" + size +
", capacity=" + capacity +
'}';
}
}
private LinkedList<Object>[]bucketsMyHashSetV1은 Integer 숫자만 저장할 수 있음Object를 사용함Object로 변경했음hashIndex()hashIndex() 부분이 변경됨
먼저 Object의 hashCode()를 호출해서 해시 코드를 찾고 찾은 해시 코드를 배열의 크기로 나머지 연산을 수행함
Object의 hashCode()를 사용한 덕분에 모든 객체의 hashCode()를 구할 수 있음
hashCode()가 호출됨hashCode()의 실행 결과로 음수가 나올 수 있는데, 배열의 인덱스로 음수는 사용할 수 없음
Math.abs()를 사용하면 마이너스를 제거해서 항상 양수를 얻을 수 있음 package collection.set;
public class MyHashSetV2Main1 {
public static void main(String[] args) {
MyHashSetV2 set = new MyHashSetV2(10);
set.add("A");
set.add("B");
set.add("C");
set.add("D");
set.add("AB");
set.add("SET");
System.out.println(set);
System.out.println("A.hashCode=" + "A".hashCode());
System.out.println("B.hashCode=" + "B".hashCode());
System.out.println("AB.hashCode=" + "AB".hashCode());
System.out.println("SET.hashCode=" + "SET".hashCode());
//검색
String searchValue = "SET";
boolean result = set.contains(searchValue);
System.out.println("set.contains(" + searchValue + ") = " + result);
}
}
실행 결과
MyHashSetV2{buckets=[[], [AB], [], [], [], [A], [B, SET], [C], [D], []], size=6,
capacity=10}
A.hashCode=65
B.hashCode=66
AB.hashCode=2081
SET.hashCode=81986
bucket.contains(SET) = true

자바 String은 hashCode()를 재정의해 두었고, 이 값을 사용하면 됨
hashIndex(Object value)에서 value.hashCode()를 호출하면 실제로는 String에서 재정의한 hashCode()가 호출됨
이렇게 반환된 해시 코드를 기반으로 해시 인덱스를 생성함
참고로 자바의 해시 함수는 단순히 문자들을 더하기만 하는 것이 아니라 더 복잡한 연산을 사용해서 해시 코드를 구함
MyHashSetV2는 Object를 받을 수 있음Member와 같은 객체도 보관할 수 있음hashCode(), equals() 두 메서드를 반드시 구현해야 한다는 점임 package collection.set;
import collection.set.member.Member;
public class MyHashSetV2Main2 {
public static void main(String[] args) {
MyHashSetV2 set = new MyHashSetV2(10);
Member hi = new Member("hi");
Member jpa = new Member("JPA"); //대문자 주의!
Member java = new Member("java");
Member spring = new Member("spring");
System.out.println("hi.hashCode() = " + hi.hashCode());
System.out.println("jpa.hashCode() = " + jpa.hashCode());
System.out.println("java.hashCode() = " + java.hashCode());
System.out.println("spring.hashCode() = " + spring.hashCode());
set.add(hi);
set.add(jpa);
set.add(java);
set.add(spring);
System.out.println(set);
//검색
Member searchValue = new Member("JPA");
boolean result = set.contains(searchValue);
System.out.println("set.contains(" + searchValue + ") = " + result);
}
}
실행결과
hi.hashCode() = 3360
jpa.hashCode() = 73690
java.hashCode() = 3254849
spring.hashCode() = -895679956
MyHashSetV2{buckets=[[Member{id='hi'}, Member{id='JPA'}], [], [], [], [], [],
[Member{id='spring'}], [], [], [Member{id='java'}]], size=4, capacity=10}
//검색
bucket.contains(Member{id='JPA'}) = true

Member의 hashCode()를 id 값을 기반으로 재정의해 둠hashIndex(Object value)에서 value.hashCode()를 호출하면 실제로는 Member에서 재정의한 hashCode()가 호출됨"JPA"를 조회할 때 해시 인덱스는 0임
여기에는 [hi, JPA]라는 회원 두 명이 있음
equals()를 사용해서 비교함따라서 해시 자료 구조를 사용할 때는 hashCode()는 물론이고, equals()도 반드시 재정의해야 함
hashCode(), equals()를 함께 재정의해 둠