equals()
매개변수로 객체의 참조변수를 받아 비교해 그 결과를 boolean으로 알려준다.
기본적으로 2개의 객체가 동일한 지 검사하기 위해 사용한다.
Person p1 = new Person(7);
Person p2 = new Person(13);
p1.equals(p2)
이 결과는 false다.
객체를 생성할 때 메모리의 비어있는 공간을 찾아 생성하기 때문에 서로 다른 두 개의 객체가 같은 주소를 갖는 일은 있을 수 없다.
하지만
p1 = p2
p1.equals(p2)
이 결과는 참이 된다. 두 개 이상의 참조변수가 같은 주소값을 같는 것은 가능하다. (p1, p2가 하나의 주소값을 가지고 있음)
그래서 v2의 주소 값을 v1이 가지게 되어 동일하다는 결과를 갖게 된다.
equals 메소드는 Object로부터 상속받아 사용되어
두 참조변수에 저장된 값이 같은 지를 판단하는 기능만 할 수 있다. (같은 객체를 참조하는가를 판단)
그렇다면 객체의 내용도 서로 같은 지를 equals 메소드가 판단할 수 있는 지 궁금할 수 있다.
이 경우, equals 메소드를 오버라이딩 해 주소가 아닌 객체에 저장된 내용을 비교하도록 변경하면 된다.
class Person{
Long id;
public boolean equals(ObBject obj){
if(obj instanceof Person)
return id == ((Person)obj).id;
return false;
}
Person(Long id){
this.id = id;
}
}
Person p1 = new Person(7);
Person p2 = new Person(13);
p1.equals(p2)
다시 이 상황을 가져와보자. 이 경우
== 을 사용하여 확인을 해보면, 둘은 다르다고 판정하지만, (주소값으로 판정, 동일성 비교)
equals를 활용하여 비교해보면, 둘은 같다는 결과를 얻을 수 있다. (객체에 저장된 내용으로 판정, 동등성 비교)
String 클래스, Date, File, wrapper(Integer,double) 의 클래스들은 주소값이 아닌 내용을 비교하도록 이미 오버라이딩 되어있다.
하지만 StringBuffer 클래스는 오버라이딩 되어있지 않다. 이 점을 유의하자!
==으로 비교가 가능한 이유
변수 선언부는 Java Runtime Data Area 의 스택 영역에 저장이 되고 해당 변수에 저장된 상수는 runtime constant pool에 저장되어 있다. 스택의 변수 선언부는 constant pool의 주소값을 가지게 되고, 다른 변수도 같은 상수를 저장하고 있다면 같은 constant pool의 주소값을 가지게 되기 때문에 주소값 비교가 되는 것이다.
hashCode()
해시함수는 찾고자 하는 값을 입력하면 그 값이 저장된 위치를 알려주는 hashCode를 반환한다.
Map의 구현체인 HashMap, HashTable, LinkedHashMap에서 사용한다. (키 타입이 기본형이 아닌 참조형인 경우)
일반적으로 해시코드가 같은 객체가 두개 이상 존재할 수 있지만, Object 클래스에서는 객체의 주소값을 기반으로 해시코드를 만들어 반환하기 때문에 32bit JVM 에서는 중복이 되지 않는다.(64bit는 4byte를 해시코드로 만들어 중복이 있을 수 있다.)
만약 둘이 같은 객체라면, hashcode 메서드를 호출했을 때 해시 코드도 같아야 한다.
String 클래스는 내용이 같으면 동일한 해시코드를 반환하도록 오버라이딩 되어있어 해시 코드값도 동일하게 얻는다.
반면 Object 클래스는 heap에 저장된 객체의 메모리 주소값으로 해시코드를 만든다고 했으므로, 모든 객체에 대해 항상 다른 Integer 해시코드값을 반환한다. 이는 System.identityHashCode로 두 객체를 비교하면 결과가 값이 다르게 나올 수 있음을 확인할 수 있다. 해시코드는 같지만 서로 다른 객체임을 알 수 있게 해준다. (실행시 마다 해시코드 값은 달라질 수 있다.)
참고
1. hashMap 이나 hashSet과 같은 클래스에 저장할 객체라면 반드시 hashcode메서드를 오버라이딩해서 사용해야 한다.
2.
public native int hashCode();
여기서 native 키워드는 메소드가 JNI라는 native code를 이용해 구현되었음을 의미한다. native는 메소드에만 적용 가능한 제어자로, 자바가 아닌 언어로 구현된 부분을 JNI를 통해 자바에서 이용하는 경우 사용한다.
둘을 같이 재정의해야 하는 이유
동일한 객체는 동일한 메모리 주소를 갖는 다는 것을 의미하고, 동일한 해시코드를 가져야 한다.
그래서 클래스의 인스턴스 변수 값으로 객체의 같고 다름을 판단해야 하는 경우라면 equals메서드 와 hashCode메서드 모두 적절히 오버라이딩 해야 한다.
만약 hashCode 만 재정의하지 않는다면, 같은 값을 가진 객체여도 해시값이 다른 경우 해시 테이블에서 객체가 저장된 버킷을 찾을 수 없다.
반면, equals()를 재정의 하지 않으면 hashCode()로 객체가 저장된 버킷을 찾을 수 있지만, 해당 객체가 자신과 같은 객체인 지 비교할 수 없어 null을 리턴한다.
추가참조
toString() 에 대해서도 알아보기
자바 출력하는 방법 (toString()을 재정의하자)
역시 구글링은 스택 오버플로우가 답이었다. 기본적으로 출력을 할때는 여태 보았던 주어지는 Arrays.toString()을 사용해서 출력을 한다. toString()은 사실 기본적으로 이렇게 구성되어 있다. public Str
handmadecoding.tistory.com
출처
https://jisooo.tistory.com/entry/java-hashcode와-equals-메서드는-언제-사용하고-왜-사용할까
https://mangkyu.tistory.com/101
자바의 정석
'Java' 카테고리의 다른 글
| JVM (0) | 2022.11.24 |
|---|---|
| SOLID 객체 지향 설계 (0) | 2022.10.13 |
| [JAVA] 공부하다 정리하는 기본 개념 2 (0) | 2022.08.18 |
| [JAVA] 공부하다 정리하는 기본 개념 1 (0) | 2022.07.14 |