
코딩 테스트 문제를 풀다가 다음과 같은 코드를 작성했는데 내가 예상하지 못하는 결과가 나왔다.
...
List<Set<Integer>> pCard = new ArrayList<>();
for (int i = 0; i < 3; i++) {
pCard.add(new HashSet<>());
}
... // 대충 pCard.get(num).add 하는 코드
while(true){
...
Set<Integer> temp = pCard.get(i);
for (int j = 0; i + j * 3 < N; j++) {
if (temp.contains(card[i + j * 3])) {
temp.remove(card[i + j * 3])
}
}
...
}
코드를 보면 Set<Integer> 를 List로 여러개 만들고 확인되는 값이 있으면 지워서 중복으로 되는 경우를 제거할 수 있도록 했다.
하지만, 반복문을 돌면서 원래 pCard의 Set이 remove가 되어 제대로 결과를 출력하지 못하게 되는데..
Set<Integer> init = new HashSet<>();
init.add(1);
init.add(2);
init.add(3);
System.out.println("init = " + init);
Set<Integer> test = init;
test.remove(3);
System.out.println("init = " + init);
출력 결과
init = [1, 2, 3]
init = [1, 2]
결과를 보면 분명 test의 있는 3을 remove했는데 init에도 결과가 반영된 것을 볼 수 있다. 그렇다면 왜 그런걸까?
문제에 대한 답은 객체 참조에서 알 수 있다.
객체 참조에 대해서 설명하기 전에 용어에 대해서 짚고 넘어가자
Heap 메모리에 저장되는 실제 데이터를 말한다. 예를 들어, new String("Hello") 와 같은 명령어는 Heap 메모리에 "Hello"라는 문자열 객체를 생성한다.
객체가 Heap 메모리에 저장된 위치를 가리키는 변수를 말한다. 참조는 객체의 메모리 주소를 저장하지만, Java에서는 직접 주소를 다룰 수 없고 참조 변수를 통해서만 접근이 가능하다.
String greeting = new String("Hello");
즉, 다음과 같은 코드에서 greeting은 위치(주소), "Hello"는 Heap 메모리에 있는 데이터인 것이다.
Set<Integer> init = new HashSet<>();
init.add(1);
init.add(2);
init.add(3);
System.out.println("init: " + init);
System.out.println("init hash: " + System.identityHashCode(init));
Set<Integer> test = init;
System.out.println("test hash before modification: " + System.identityHashCode(test));
test.remove(3);
System.out.println("init after modification: " + init);
System.out.println("test hash after modification: " + System.identityHashCode(test));
출력 결과
init: [1, 2, 3]
init hash: 288665596
test hash before modification: 288665596
init after modification: [1, 2]
test hash after modification: 288665596

객체가 참조하는 주소값을 출력해보면 다음과 같다. 288665596으로 test는 init과 같이 같은 주소를 참조하고 있는 것을 확인할 수 있다.
그래서 test를 수정하더라도 실제로 init이 가리키는 heap 메모리에 영역의 데이터를 수정하기 때문에 결과가 반영되는 것이다.
identityHashCode?
객체 고유한 해시 코드를 반환하는 메서드로, 객체의 메모리 참조 기준으로 생성되는 hashCode이다.
...
Set<Integer> test = new HashSet<>();
test = init;
...
새로운 객체를 만들고 기존 데이터를 할당하면 어떻게 될까?
결론부터 말하면 Variable 'test' initializer 'new HashSet<>()' is redundant 다음과 같은 메세지를 출력한다. 대충 변수 test 초기화가 중복된다는 의미인데 자세한 이유는 다음과 같다.
Set<Integer> test = new HashSet<>();
test = init;
대학 1학년, 객체 지향 프로그래밍 수업에서 C++ 로 지겹도록 배웠던 개념인데 자바를 계속 사용하면서 기본적인 것을 잊고 있었던 것 같다.😢