성동일은 아는데, 동일성은 뭘까? ㅋㅋ
(죄송합니다)
동일성(Identity)은 두 객체의 메모리 주소가 같음을 의미한다.
동등성(Equality)은 두 객체의 값이 같음을 의미한다.
즉, 동일성은 유전자까지 똑같고, 동등성은 겉보기만 똑같은 것이다!
동일성은 나와 내 복제인간이라면, 동등성은 나와 내 도플갱어 쯤?
java 에서 동일성은 ==
로, 동등성은 equals
로 확인 가능하다!
코드로 확인해보자!
void 동일성() {
List<String> crews1 = List.of("ash", "ako", "maco");
List<String> crews2 = crews1;
System.out.println(crews1 == crews2); // true
System.out.println(crews1.equals(crews2)); // true
}
void 동등성() {
List<String> crews1 = List.of("ash", "ako", "maco");
List<String> crews2 = List.of("ash", "ako", "maco");
System.out.println(crews1 == crews2); // false
System.out.println(crews1.equals(crews2)); // true
}
첫번째 동일성 테스트에선, crew2에서 리스트를 새롭게 선언하지 않고 기존에 있던 리스트 crew1을 대입받는다.
즉, crew1과 crew2는 메모리상에서 같은 주소를 바라보고 있다.
반면 두번째 동등성 테스트에선, crew2에서 리스트를 새롭게 선언해 crew1과 crew2는 메모리상 다른 주소를 바라보고 있다.
동일성이 지켜지면, 동등성은 자연스럽게 따라서 지켜진다. (내 복제인간은 겉모습도 나랑 똑같다.)
하지만 동등성이 지켜진다해서, 동일성이 지켜지진 않는다. (겉모습이 나랑 똑같다고 내 복제인간인건 아니다. 내 도플갱어일지도?)
(뭔가 중고등학생때 명제의 예시로 쓰일 것만 같다 ㅎㅎ)
문자열(String)의 동일성 / 동등성
그럼 여기서 퀴즈!!
(아코가 나한테 냈는데, 난 틀렸다..)
String name1 = "ash";
String name2 = "ash";
System.out.println(name1 == name2); // true? false?
여기서 출력값은 true일까 false일까?
정답은...
true이다!!
Java에서 문자열은 String Pool로 관리되므로, "ash"라는 두 개의 String 변수를 선언하더라도
JVM Heap 메모의 String Pool에는 "ash"라는 문자열 하나만 존재한다..!
즉, name1과 name2는 메모리상 같은 주소를 바라본다 (동일성)
그러면 메모리상 다른 주소를 바라보게 하고싶으면 어떻게 해야할까?
String name1 = "ash";
String name2 = new String("ash");
System.out.println(name1 == name2); // false
이렇게 new 연산자를 이용해 생성해주면, 같은 값이더라도 힙 영역에 새로운 객체가 생성된다.
equals()와 hashcode() 오버라이딩
equals() 오버라이딩
java에서 동등성을 검사하기 위해선, equals()
를 사용해라 했는데
사실 override 되지 않은 equals()
는 ==
과 동일하게 사용된다! (원시값의 경우 내용 비교, 객체의 경우 주소 비교)
따라서 객체의 동등성을 비교하기 위해선 equals()
를 오버라이딩해서 사용해야한다.
class Crew {
private final String name;
public Crew(String name) {
this.name = name;
}
}
void 객체_동등성_테스트() {
Crew crew1 = new Crew("ash");
Crew crew2 = new Crew("ash");
System.out.println(crew1.equals(crew2)); // false
}
오버라이딩 하지 않았을 때 동등성 검사는 false인데,
여기서 다음과 같이 equals()
함수를 오버라이딩 해주면, 객체 동등성 테스트값은 true가 된다
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Crew crew = (Crew) o;
return Objects.equals(name, crew.name);
}
hashCode() 오버라이딩
Crew 리스트를 만들고 사이즈를 확인해보자!
List<Crew> crews = new ArrayList<>();
crews.add(new Crew("ash"));
crews.add(new Crew("ash"));
System.out.println(crews.size()); // 2
예상했던 것과 같이 2가 나온다.
중복되지 않는 Crew 인원수를 알고싶다면, List에서 중복값을 허용하지 않는 Set
자료구조를 사용하면 된다.
Set<Crew> crews = new HashSet<>();
crews.add(new Crew("ash"));
crews.add(new Crew("ash"));
System.out.println(crews.size()); // 2
하지만 1이 나올것이라는 예상과 달리, 2가 나왔다.
그 이유는 HashMap / HashSet / HashTable과 같은 객체들을 사용하는 경우 해쉬값을 비교해서 동등성을 비교한다.
이 문제를 해결하기 위해 hashCode()를 오버라이딩 해주자.
@Override
public int hashCode() {
return Objects.hash(name);
}
🤔 전 Hash 자료구조를 안쓸건데, 꼭 hashCode를 오버라이딩 해야할까요?
무조건 해야하는건 아니지만.. (뭐 오류가 나거나 그러진 않는다)
요구사항은 언제나 바뀔 수 있다.
미래를 대비해 hashCode를 오버라이딩하는 습관을 들이자!
'프로그래밍' 카테고리의 다른 글
[Java] LinkedHashMap (0) | 2023.03.13 |
---|---|
[Java] 함수형 인터페이스와 람다 (5) | 2023.03.12 |
[Java] 복사와 불변 (new, unmodifiable, copyOf) (2) | 2023.02.24 |
JUnit5란? (0) | 2023.02.20 |
AssertJ를 사용하는 이유 (0) | 2023.02.17 |