개발잡소리

[ 개발 잡소리 ] HashCode와 String (Java 잡소리)

vcs 2024. 7. 25. 13:49

Java의 System객체에는 IdentifyHashCode()이 있고

모든 Object를 상속받은 자료형에는 HashCode()라는 기본함수가 들어있다.

 

뭐가 다른 걸까?

System.IdentifyHashCode()

이 녀석이 뭐 하는 놈이냐면 () 안에 들어온 객체의 메모리를 기반으로 하는 해시코드를 뽑아서 반환해 준다.

임의로 막 적은 숫자임

단순히 주소를 보기 쉽게 숫자로 싹 한번 바꿔서 보여주는 애라고 알면 된다.

따라서 동일한 객체라도 두 번 생성되면

다른 메모리 주소를 가지므로 다른 identifyHashCode해쉬를 반환한다.

 

HashCode()

그러면 이 해시코드란 놈은 뭐 하는 놈인가 기본적으로 해시코드는 객체의 내용에 따라서 생성된다.

Object클래스에서 상속받아 구현된 메서드로 모든 객체에서 사용할 수 있는

기본함수( HashCode() )로써 사용할 수 있다.

 

그래서 주소가 아닌 객체의 내용에 따라서 생성되기 때문에 Equals 메서드와 함께 사용되며,

컬렉션들을 비교하는 데에 사용된다. 결론적으로 개발자들이 이 메서드를 재정의 하여 객체에

특정내용에 기반한 해시코드를 생성하도록 구현할 수 있는 것이다!


 

그래서 이 둘을 비교할 때 쓰이는 대표적인 예시로 String으로 몇 가지를 보여주는데

이 테스트로 신기한 사실을 한 가지 더 알 수 있다.

// 1
String str1 = "java";
String str2 = "java";

// 2
String str3 = new String("java");
String str4 = new String("java");

System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
System.out.println("");
System.out.println(System.identityHashCode(str3));
System.out.println(System.identityHashCode(str4));

 

이렇게 할당된 두 종류의 변수들이 있다고 치자,

 

이들을 각각 IdentifyHashCode로 출력해 보면

이렇게 나온다. 분명 다 같은 "java"를 넣은 것 같은데 왜 어떤 건 똑같고 어떤 건 다를까?

앞서 IdentifyHashCode는 

라고 말했었다.
따라서 이건 메서드 문제가 아니라 메모리에 할당된 위치가 다르기 때문이다.

str3, 4는 new String생성자를 통해 Heap메모리에 새롭게 객체로써 할당되었기 때문에

메모리 위치가 다른 것이다.

 

어 그럼 같은 놈은 뭔가 라는 생각이 들것이다

이것은 Java의 문자열 상수 풀(string constant pool)과 연관이 있다.

JVM은 이런 문자열 리터럴을 생성할 때 문자열 상수 풀이라는 곳에 저장한다.

따라서 나중에 동일한 문자열이 다시 재사용될 때에는 새로운 객체를 생성하지 않고 상수 풀에 저장된

기존의 객체를 가져와서 사용하기 때문에 주소 해쉬를 찍었을 때 이것이 같게 나오는 것이다

 

그렇다면 HashCode를 뽑으면 어떻게 나올까?

System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println("");
System.out.println(str3.hashCode());
System.out.println(str4.hashCode());

이렇게 나온다.

HashCode()는 객체의 내용에 따라서 해시를 생성해 준다고 했다

따라서 우리는 모두 str1 ~ str4에 모두 같은 "java"를 넣었기 때문에 같은 해시코드가 4번 출력된  것이다.

 

이것에 대한 문제는 문자열을 비교하면서 느껴볼 수 있는데

String str1 = "java";
String str2 = new String("java");

System.out.println("str1 == str2: " + (str1 == str2)); 
System.out.println("str1.equals(str2): " + str1.equals(str2));

이를 실행시키면 

결과가 다른 것을 볼 수 있다

 

따라서 우리가 사용하는 ==과 equals()는 비교하는 조건이 다르다는 것을 알 수 있다.

== 는 메모리 위치에 따른 해쉬를 비교하여 false가 나오게 된다.

"java"는 문자열 상수 풀에, new String("java")는 Heap영역에 할당되었기 때문에 메모리상 위치가 다르다.

 

equals()는 다르다. 메모리 위치와는 상관없이 문자열 객체 자체의 해쉬를 뽑아

문자하나하나가 같은지를 비교한다.

 


이렇게 IdentifyHashCode는 무엇이고 HashCode는 무엇이고 뭐가 다른지와,
== 와 equals()가 어떤 상황에서 다르게 동작할 수 있는지를 알아보았다.

적절한 곳에 제대로 알고 사용하도록 하자.