.equals와 .hashCode()
Object 메서드에 있는 .hashCode() 메서드는 해당 객체의 주소값을 이용하여 만든 객체만의 고유한 정수 값을 가진다. equals()를 오버라이딩 할 때에는 반드시 hashCode()도 동일한 결과를 내도록 함께 오버라이딩 해주어야 한다.
차근 차근 알아보기.
Card a = new Card('트럼프카드',10)
Card b = new Card('트럼프카드',10)
a == b // a와 b는 내용만 같을 뿐, 다른 객체라서 False가 나온다.
a.equals(b) // 이 결과가 True 나오게 오버라이딩했다면
a.hashCode() == b.hashCode() // 이 결과도 True가 나오도록 오버라이딩해야 한다.
🔴 동일성(identity)과 동등성(equality)
동일성은 완전히 같은 것을 의미, 동등성은 다른 객체지만 가지고 있는 값(정보)가 같음을 의미함
동일하다의 경우 == 연산자로 비교하고,
동등하다의 경우 equals 연산자로 비교한다.
즉, 객체간 == 연산자는 주소값의 비교, equals는 내용의 비교로 사용한다.
🔴 == 연산
( == ) 연산은 값 자체를 비교하는 연산이다. 레서런스 타입인 경우 변수에는 값이 아닌 심볼이 들어있기 때문에 동등성이 아닌 동일성을 비교한다.
사실 자바에서는 int, double 같은 기본형 타입도 레퍼런스 타입으로 관리되는 경우가 있음.
레퍼런스로 만들고 Constant Pool 안에 같은 객체를 참조하도록 만들어 최적화 시킬 수 있음.
-> Constant Pool : 런타임에 생성되는 static 상수 저장소. Constant Pool 객체에 저장되며 아래와 같은 상수 객체를 가짐
참고로 상수 풀은 Method Area, 즉 정적영역에 있는 메모리이기에 GC의 대상이 아니다.
하지만 상수 값이 아닌 상수 레퍼런스를 비교한다고 하더라도 ( == )를 이용해서 값을 비교한다. 어차피 같은 상수 객체를 참조하기에 레퍼런스를 비교해도 값이 같이 때문이다.
🔴 .equals()
Object.equals()의 원형은 ( == ) 연산으로 만들어져 있다. 즉, 객체의 주소 값을 비교한다.
// Object.equals의 원형.
public boolean equals(Object obj) {
return (this == obj);
}
equals는 동등성(equality)을 비교하는데 사용한다. 하지만 원형을 보면 Object객체가 객체의 값을 막 분석해 자동을 비교해주는 것은 아니다. 필요에 따라 직접 equals() 객체를 상속하여 구현해야한다.
🔴 String.equals()
String도 마찬가지이다 단순히 주소값 비교( == )를 하게 되면 Constant pool에 있는 상수 스트링은 정상 비교가 되겠지만, new를 이용하여 String 객체를 비교하면 주소값이 다르기에 정상 비교되지 않는다.
그래서 String 구현부를 보면 Object.equals()를 오버라이딩하여 새롭게 정의한 것을 볼 수 있다. String.equals() 메서드는 객체가 가진 문자열을 한 글자씩 비교하여 동일하다면 true를 반환하도록 오버라이딩 되어있다.
🔴 객체의 hashCode()
객체가 가진 hashCode는 일종의 객체 지문이라고 생각하면 된다.
그 객체의 값을 대표하는 건데, 기본 구현은 객체의 주소에 hash함수를 적용시켜 만든다. 그래서 이름이 해쉬코드이다.
참고로 Object.hash.Code()는 native메서드이다.
clas Person { ... }
public class Main {
public static void main(String[] args) {
Person person1 = new Person("kim");
Person person2 = new Person(new String("kim"));
Person person3 = person2;
System.out.println("person1.hashCode() = " + person1.hashCode());
System.out.println("person2.hashCode() = " + person2.hashCode());
System.out.println("person3.hashCode() = " + person3.hashCode());
// person1.hashCode() = 1349414238
// person2.hashCode() = 321142942
// person3.hashCode() = 321142942 주소가 같으면 hashCode도 같다.
}
}
🔴 String의 hashCode()
String의 경우는 조금 다르다.
String 객체가 가진 주소값이 아니라 문자열에서 한 글자씩 가져와 변환하여 String 객체간의 동일성을 비교하는 hashCode()를 작성한다.
즉, 주소가 아닌 문자열 내용 자체를 비교( =동등성 비교) 하는 hashCode를 생성하는 셈이다.
참고로 자바는 32비트 운영체제만 사용할 때 개발된 언어라 32비트 주소값을 이용해서 해쉬함수를 만든다. 64비트 운영체제의 경우 주소값에 절반만 사용하는데, 알고리즘으로 최대한 겹치지 않게 만들었어도 정말 정말 낮은 확률로 같은 주소값이 2개 나올 수 있다는걸 상식으로 알고만 있자. 주소값이 같으면 당연히 해쉬코드의 값도 같다. (그런데 사실상 일어날 확률이 0에 가깝고 64비트로 바꿨을 때 성능이 하락해서 그대로 32비트 주소체계로 사용하고 있다고 한다.)
🔴 equals()와 hashcode()의 동작은 같아야한다.
일종의 규칙이다.
지키지 않는다고 해서 프로그램이 멈추거나 컴파일 타임 예외가 발생하는 것은 아니다.
이렇게 해야하는 이유는 코드의 확장성 같은 추상적인 이유도 있지만 제일 중요한 것은 자바 표준 라이브러리, 컬렉션 프레임워크가 hashCode()와 equals()를 둘다 사용하기 때문이다.
컬렉션 프레임워크에서 사용하는 hashCode()와 equals()를 살펴보자.
HashMap에서 키 값을 새로 입력할 때, 중복된 키가 있는지 비교하는 연산을 한다고 가정하기.
hashCode()
메서드를 실행해서 리턴된 hashCode() 값이 같은지 본다. 만약 다르면 다른 객체로 판단한다.hashCode() -> equals
hashCode의 값이 같다면 equals(~)로 다시 비교한다. 이 두개가 모두 맞아야 동등한 객체로 판단한다.
만약 hashCode의 값이 다르다면 비교 연산 자체를 하지 않음
만약 equals()를 변경하지 않았다면, ( == ) 를 이용해서 동일성 비교를 하기 때문에 해당 작업에 아무런 문제가 없다.
하지만 객체의 비교를 위해 equals()를 수정했다면 반드시 hashCode() 비교도 같은 동작을 하도록 해시함수를 수정해야한다.
🔴 결론 : equals를 재정의 하려거든 hashCode도 재정의 해야한다.
-> 동일성( == )이 아닌 동등성(.equals)을 비교하려면 Object.equals()를 오버라이딩 해야한다.
단, equals()를 오버라이딩했다면 반드시 hashCode() 비교도 동일한 동작을 하도록 오버라이딩한다,
즉, A.equals(B) 가 true라면 A.hashCode() == B.hashCode() 도 true가 되도록 만들어야 한다.
Card a = new Card('트럼프카드',10)
Card b = new Card('트럼프카드',10)
a == b // a와 b는 내용만 같을 뿐, 다른 객체라서 False가 나온다.
a.equals(b) // 이 결과가 True 나오게 오버라이딩했다면
a.hashCode() == b.hashCode() // 이 결과도 True가 나오도록 오버라이딩해야 한다.
참고자료 : [https://jiwondev.tistory.com/113](https://jiwondev.tistory.com/113)
'Java' 카테고리의 다른 글
immutability(불변성) (1) | 2024.03.07 |
---|---|
Arrays.sort() Collections.sort()에서 사용되는 알고리즘 (1) | 2024.03.06 |
자바 가상 머신 JVM(Java Virtual Machine) (0) | 2024.02.27 |
String, StringBuffer, StringBuilder (1) | 2024.02.26 |
[점프 투 자바] 7장 자바 날개달기2 (1) | 2024.01.01 |