본문 바로가기
Java

[Java] hashCode() 메소드

by dbjh 2020. 1. 12.
반응형

자바의 hashCode() 메소드가 어떤 기능을 하는지 확인해보도록하자.

Java의 Hash Code란, 객체를 식별할 수 있는 유니크한 값을 말한다. 메모리에 생성된 객체의 주소를 정수로 변환한 형태를 얘기하는데, 이 정수는 중복되지 않는 고유의 값입니다.

Object Class에서 hashCode()를 가지고 있어서 객체를 생성하여 확인해보면 Hash Code 값을 확인 할 수 있습니다.

Animal.java
public class Animal {	
}

위의 Animal 클래스의 객체를 여러개 생성하여 Hash Code를 확인해보도록하자 .

HashCodetest
public class HashCodeTest {

	public static void main(String[] args) {
		
		Animal animal1 = new Animal();
		Animal animal2 = new Animal();
		Animal animal3 = new Animal();
		
		System.out.println(animal1.hashCode());
		System.out.println(animal2.hashCode());	
		System.out.println(animal3.hashCode());	
	}
}

 

위처럼 코드를 작성하고 실행 시켜보면, 

모두 다른 Hash Code 값

모두 다른 값인 것을 확인할 수 있다.

실질적으로 Java에서 정의된 hashCode() 메소드는 아래와 같이 설명된다.

1. 변경되지 않은 한 객체의 hashCode() 메소드 호출결과는 항상 같은 integer 값이어야 한다.
2. equals 메소드가 같다고 판별한 두 객체의 hashCode() 호출 결과는 똑같은 integer 값이어야 한다.
3. 그러나 java.lang.Object.equals 메소드가 다르다고 두 객체의 hashCode() 값이 반드시 달라야 하는 것은 아니다.

위의 3번처럼 예외가 되는 경우가 있는데, HashSet, HashMap, HashTable은 아래의 그림과 같은 방법으로 두 객체가 동등한지 비교한다.

객체 비교 플로우

처음에 hashCode() 메소드의 결과 값이 동일한지를 확인한다. 그런다음 Hash Code 값이 다르면 서로 다른 객체로 판단하고, Hash Code 값이 같으면 equals()메소드로 다시 비교한다. 두 조건이 모두 맞아야 동일한 객체로 판단한다.

다음 예제를 보면 Key클래스 equals() 메소드를 재정의해서 number필드값이 같으면 true를 리턴하도록했다. 그러나 hashCode() 메소드는 재정의 하지 않았기 때문에 Object의 hashCode() 메소드가 사용된다.

 

Key.java
public class Key {
	public int number;

	public Key(int number) {
		this.number = number;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Key) {
			Key compareKey = (Key) obj;
			if (this.number == compareKey.number) {
				return true;
			}
		}
		return false;
	}
}

이런 경우 두개의 동등한 객체를 HashMap의 식별키로 Key 객체를 사용하면 저장된 값을 찾아 오지 못한다. 왜냐하면 number 필드값이 같더라도 hashCode() 메소드에서 리턴하는 해시코드가 다르기 때문에 다른 식별키로 익식하기 때문이다. 실제 예제를 실행해보고 확인해보자.

 

KeyExample.java
import java.util.HashMap;

public class KeyExample {
	public static void main(String[] args) {

		// Key 객체를 식별키로 사용해서 String 값을 저장하는 HashMap 객체생성.
		HashMap<Key, String> hashMap = new HashMap<Key, String>();

		// 식별키 "new key(1)"로 "홍길동"을 저장함
		hashMap.put(new Key(1), "홍길동");

		// 식별키 "new key(1)"로 "홍길동"을 읽어옴
		String value = hashMap.get(new Key(1));
		System.out.println(value);
	}
}

HashMap 참조 결과

위에 내용에서 Hash Code를 정의하지 않았기 때문에 값은 null이 조회될 것이다. 만약 의도한 대로 홍길동을 읽으려면 아래와 같이 hashCode를 재정의 해야 한다.

Key.java
public class Key {
	...
	@Override
	public int hashCode() {
		return number;
	}

}

Key 클래스에 위처럼 hashCode() 메소드를 재정의 한 후에 다시 실행해 보도록하자.

hashCode() 재정의 후 HashMap 참조 결과

저장할 때의 new Key(1)과 읽을때의 new Key(1)은 다른 참조값의 다른 객체지이지만 HashMap은 hashCode()의 리턴값이 같고, equals() 리턴값이 true가 나오기 때문에 두 객체는 동등 객체로 평가하여 하나의 키처럼키 사용하게 된다. 즉, 같은 식별키로 인식한다는 뜻이다. 이러한 이유로 객체의 동등 비교를 위해서는 Object의 equals() 메소드만 재정의하지 말고 hashCode()메소드도 재정의해서 논리적 동등 객체일경우 동일한 해시코드가 리턴되도록 해야한다.

참고로 String 클래스 같은경우 hashCode() 메소드의 리턴 값이 같으려면 동일한 문자열 이어야한다.

 

  • 참조

https://johngrib.github.io/wiki/Object-hashCode/

https://minwan1.github.io/2018/07/03/2018-07-03-equals,hashcode/

http://www.hanbit.co.kr/store/books/look.php?p_code=B1460673937

반응형

댓글