equals가 뭐지?
그거 그냥 문자 문자 비교하는 거 아님?
아무 생각 없이 그냥 문자열 비교하는 메소드라고 생각하면서 사용했다. 하지만 어떤 용도로 사용하는지 무슨 메소드인지 공부하고 나서 나 자신이 부끄러워졌다. 그냥 생각 없이 쓰기만 하는구나. 그래서 내가 공부한 내용을 정리해 보고자 한다.
Object
Java의 최상의 클래스는 Object이다.
모든 클래스는 Object를 상속받아서 사용한다.
equals & hashCode & toString 같은 메소드도 전부 Object의 기능들이다.
equals
Object의 선언되어있는 기능을 보면 해당 오브젝트(객체가 같은 객체인지 확인해서 Booleann 타입으로 return 해준다.
어?
같은 객체인지 비교하는 거라면 같은 문자열을 비교하는 것이 아니었네? 라는 의문점들이 들게 된다.
소스를 통해 확인해보자.
data1, data2는 서로 다른 객체니 else의 결과값이 나올 것이다.
하지만 실행 결과는 전부 알듯이 둘이 같은 객체라고 말하고 있다.
그 이유는 String.class는 Object를 상속받아 equals() 함수를 재정의하고 있다.
둘이 같은 객체여도 true로 리턴하고, 두 개의 문자열을 하나씩 비교해서 같아도 true로 리턴시켜버린다.
그래서 우리는 String.class가 Object의 equals를 재정의해서 사용하고 있다는 사실을 알 수 있다.
하지만 재정의를 하지 않고 객체를 비교한다면 어떤 일이 일어날까?
id와 name 이 같은 데이터를 가진 두 객체를 비교한다고 해도 Object의 equals()에 비교를 통해 false의 값이 나올 것이다.
두 객체의 값이 같을 때 true가 나오게 하려면 equals를 재정의 해야 한다.
equals 재정의
Id의 값이 일치해도 true로 리턴하게 재정의를 해주었다.
equlas는 언제 재정의를 해야할까?
객체 식별성(object identity, 두 객체가 물리적으로 같은가)이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의되지 않았을 때이다.
주로 값 클래스들이 여기 해당하며, 값 클래스란 Integer와 String처럼 값을 표현하는 클래스를 말한다.
equals가 논리적 동치성을 확인하도록 재정의해두면, 그 인스턴스는 값을 비교할 수 있음과 동시에 Map의 키와 Set의 원소로 사용할 수 있게 된다.
equals 메서드를 재정의할 때는 반드시 일반 규약을 따라야 한다.
반사성(reflexivity) : x.equals(x)는 true
대칭성(symmetry) : x.equals(y)가 true이면 y.equals(x)도 true
추이성(transitivity) : x.equals(y)는 true이고 y.equals(z)는 true이면 x.equals(z)는 true
일관성(consistency) : x.equals(y)를 반복해서 호출해도 항상 true 또는 false를 반환
null-아님 : x.equals(null)는 false
equals 규약을 어기면 그 객체를 사용하는 다른 객체들이 어떻게 반응할지 알 수 없다.
주의사항
equals를 재정의할 땐 hashCode도 반드시 재정의해야 한다
꼭 필요한 경우가 아니면 equals를 재정의하지 말자. 대부분 Object의 equals는우리가 원하는 비교를 해준다.
hashCode
hashCode란 객체의 유일한 integer값을 반환한다.
equals를 재정의 했다면 hashCode도 같이 재정의 해주자.
동일한 객체를 가진다는 의미는 동일한 hashCode를 가진다는 의미다.
hashCode규약
Java 프로그램을 실행하는 동안 equals에 사용된 정보가 수정되지 않았다면, hashCode는 항상 동일한 정수값을 반환해야 한다. (Java의 프로그램을 실행할 때 마다 달라지는 것은 상관이 없다.)
두 객체가 equals()에 의해 동일하다면, 두 객체의 hashCode() 값도 일치해야 한다.
두 객체가 equals()에 의해 동일하지 않다면, 두 객체의 hashCode() 값은 일치하지 않아도 된다.
equals를 통해 둘이 같은 객체인데도 불구하고 hashCode의 값이 다르기 때문에 값을 get 하지 못하는 현상이 일어난다.
마찬가지로 put하는 상황에서도 같은 객체이더라도 hashCode 값이 다르기 때문에 두개의 객체가 다른 위치에 저장이 될것이다.
이는 해쉬키 충돌을 일으킬 수 도있고 메모리 낭비가 될 수도 있다.
HashMap에 대해 보고오면 해당 내용을 더 이해하기 쉬울것이다.
https://velog.io/@jaejun/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0JAVAHashMap%EC%9D%B4%EB%9E%80
그래서 우리는 equals를 재정의 했다면 hashCode도 재정의를 해야한다.
hashCode 재정의
hashCode를 재정의하므로 동일한 객체의 hashCode의 값도 동일한 code 값을 가질 수 있게 된다.
재정의하기전에 hashCode 값
재정의후 hashCode 값
정리
- equals()와 hashcode()는 항상 같이 재정의 한다.
- equals를 재정의할 때의 주의사항
- 꼭 필요한 경우가 아니면 equals를 재정의하지 말자
참고자료
- https://velog.io/@sonypark/Java-equals-hascode-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C
- https://mangkyu.tistory.com/101
- https://velog.io/@banjjoknim/%EC%95%84%EC%9D%B4%ED%85%9C-10.-equals%EB%8A%94-%EC%9D%BC%EB%B0%98-%EA%B7%9C%EC%95%BD%EC%9D%84-%EC%A7%80%EC%BC%9C-%EC%9E%AC%EC%A0%95%EC%9D%98%ED%95%98%EB%9D%BC