본문 바로가기

Language/JAVA

‘==’ vs equals() vs hashcode()

Java 프로그래밍에서 값이 같은지 비교하려면 어떻게 해야할까요?

숫자나 불리언 타입의 경우에는 ‘==’로 비교하면 되요!

그럼 값이 같은지 확인해주거든요.

 

그런데 문자열이나 날짜, 파일, 그 외의 객체(Object)를 비교할 때도 ‘==’을 사용하면 될까요?

이런 객체에서 ‘==’을 사용하게 되면 ‘False’가 나올거에요.

왜 그럴까요?

 

먼저 ‘== ‘ 비교에 대해 알아볼게요.

 

‘==’은 값을 비교해줘요.

그런데 앞에서 말 했듯이 객체(Object)의 값을 비교할 때에는 false가 나오기도 하죠.

 

그 이유는 int, byte, short, long, float, double, boolean, char 등 Primitive Type의 변수에 대해서는

저장된 값을 비교하게 되지만

 

String을 포함한 Reference Type의 객체(Object)에 대해서는 주소 값을 비교해요.

그러니 할당된 값은 같지만 서로 다른 String 객체를 생성한 것이니 주소 값이 다르겠죠?

 

그럼 코드로 확인해볼까요?

 

1| // Primitive Type
2| int a = 10;
3| int b = 10;
4| int c = 30;
5| System.out.println(a==b);    // 10 == 10
6| System.out.println(b==c);    // 10 == 30
7| > (출력) true
8| > (출력) false

 

먼저 Primitive Type!

 

a(10)와 b(10)의 값이 같으니까 당연히 true가 출력되겠죠?

마찬가지로 b(10)와 c(30)의 값은 다르니까 false가 나와요.

 

Primitive Type은 ‘==’비교를 통해 값이 같은지 확인할 수 있어요!

 

다음으로는 Reference Type을 볼건데요.

 

1 | // Reference Type
2 | String str1 = "Hello World";
3 | String str2 = "Hello World";
4 | String str3 = new String("Hello World");
5 | String str4 = str3;
6 | System.out.println(str1 == str2);
7 | System.out.println(str2 == str3);
8 | System.out.println(str2 == str3);
9 | > (출력) true
10| > (출력) false
11| > (출력) true

 

어라? 첫번째 결과가 true가 나오네요?

분명 Reference Type은 주소값을 비교한다고 했는데 왜일까요?

 

먼저 얘기할 것은 String에서 ‘==’ 비교는 저장된 값을 비교하는게 아니에요.

Reference Type에서 분명 주소값을 비교해서 같은 객체(Object)인지 알려줘요.

 

그럼 여기서 생각해 볼 수 있는 것은 str1과 str2는 같은 객체(Object)라는 것!

Java에서 str1과 str2를 같은 객체로 인식하기 때문에 true가 나오는거에요.

 

이때 str2의 값은 “Hello World!”로 바꾸면?

값이 바뀌면서 새로운 객체로 인식하기 때문에 str1과 str2는 ‘==’ 비교를 하게 되면

false 값이 나와요.

 

그럼 str3은? new 키워드를 통해 새로운 객체(Object)를 생성해줬어요.

그럼 str1과 str3에 저장된 값은 다를까요?

 

코드에서 보면 분명이 똑같은 문장을 저장하고 있다는 것을 알 수 있어요.

하지만 str3는 새로운 객체(Object)이기 때문에 str1, str2와 ‘==’으로 비교하게 되면

false가 나오게 되는거에요.

 

그럼 srt4는?

Line 5에서 str4 = str3이라고 했으니 str3에 저장되어 있는 “Hello World”라는 값을 넣는걸까요?

 

아니에요!

여기서는 str3 객체의 주소를 str4에 넣어주는거에요.

그래서 같은 객체로 인식할 수 있는거죠.

 

이처럼 ‘==’ 비교를 통해 객체가 같은지 비교하는 것을 동일성 비교라고 해요.

 

다른 Reference Type으로 한번 더 비교해볼까요?

 

1| List<String> strList1 = new ArrayList<String>();
2| List<String> strList2 = strList1;
3| List<String> strList3 = new ArrayList<String>();
4| System.out.println(strList1 == strList2);
5| > (출력) true
6| > (출력) false

 

마찬가지로 strList2에 strList1의 주소 값을 저장하기 때문에 ‘==’ 비교를 하게 되면 true가 나와요.

하지만 strList3는 new 키워드를 통해 새로운 객체(Object)를 생성한 것이기 때문에

strList1과 주소 값이 달라지게 되요.

그래서 strList1과 비교하게 되면 false가 나오는 거에요.

 

이번에는 equals()에 대해 알아볼까요?

 

Java에서 equals는 최상위 클래스인 Object에 정의된 Method에요.

먼저 Object에 정의된 equals Method의 코드부터 볼게요.

 

public boolean equals(Object obj) {
    return (this == obj);
}

 

소스코드에서 보면 equals도 ‘==’ 비교를 하는 것을 알 수 있어요.

당연히 주소값을 비교한다는 것을 의미하겠죠.

 

그런데 String 클래스equals는 주소값이 아닌 저장된 문자열을 비교해요.

최상위 클래스인 Object를 상속받아서 equals()를 재정의(override)하고 있기 때문이에요.

String 코드를 볼게요.

 

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

 

그래서 String type은 값을 비교하고 싶으면 equals()를 이용해야되요.

앞에서 말한 것처럼 ‘==’ 비교는 주소값을 비교하기 때문에 안에 저장된 문장이 같아도

false가 나올 수 있기 때문이에요.

 

반드시 String type은 equals()로 값을 비교할 것!

 

참고로 String뿐만 아니라 Date, File, Wrapper Class들은 내용을 비교하도록 Overriding 되어있으니까 equals로 비교하세요!

 

이처럼 Override된 equals()는 저장된 값이 일치하는지를 비교하기 때문에 동등성 비교라고도 해요.

 

그럼 hashCode()는 뭘까요?

 

객체(Object)의 hashCode는 객체를 식별할 수 있는 정수 값을 의미해요.

정수 값은 객체의 메모리 주소를 반환해 정수 값으로 만드는데요.

즉, 주소 값이 같으면 hashCode 값도 같겠죠?

 

앞에서 작성한 String 코드를 다시 볼까요?

 

1 | // Reference Type
2 | String str1 = "Hello World";
3 | String str2 = "Hello World";
4 | String str3 = new String("Hello World");
5 | System.out.println(srt1.hashCode())
6 | System.out.println(srt2.hashCode())
7 | System.out.println(srt3.hashCode())
8 | > (출력) 123456
9 | > (출력) 123456
10| > (출력) 485930

 

출력된 hashCode는 예시로 작성해봤어요.

(hashCode에 대해 이해하는데 문제가 없으니 그냥 진행할게요!)

 

먼저 srt1과 str2의 hashCode가 같은 것을 볼 수 있어요.

여기서 “Hello World”가 저장된 메모리 주소가 같기 때문에 hashCode가 동일하게 나온다는 것을 알 수 있어요.

 

그리고 str3은 new 키워드를 통해 새로운 객체를 생성한 것이기 때문에 새로운 메모리 주소에 “Hello World”가 저장되어 있다는 것을 확인할 수 있죠.

 

그럼 HashCode는 언제 사용할까요?

 

먼저 같은 객체인지 비교하기 위해 hashCode를 사용할 수 있겠죠?

hashCode 값이 같으면 같은 객체라는 것을 의미하니까요!

 

그리고 객체 값의 동등성 비교 시에도 hashCode를 사용하기도 해요.

HashSet, HashMap, HashTable에서 두 객체가 동등한지 비교할 때 사용한다고 해요.

이 Collection들은 Key 값을 기준으로 HashCode를 생성하는데

HashCode의 값이 일치하지 않으면 서로 다른 객체라고 인식을 하고

HashCode의 값이 같으면 equals를 통해 다시 비교해서 등등한 객체인지 판단해요.

 

이정도로만 간단히 알고!

 

hashCode()도 재정의(Overriding) 해야되요.

 

만약 여러분들이 equals Method를 재정의한다면 hashCode도 재정의 해줘야되요.

왜일까요?

소스코드부터 볼게요.

 

HashSet<User> set = new HashSet<User>();

User user1 = new User();
User user2 = new User();
user1.setId(10);
user2.setId(10);

set.add(user1);
set.add(user2);

 

먼저 hashCode()가 재정의 되어있지 않다면?

user1과 user2은 ID는 같지만 다른 객체에요.

하지만 ID 값이 같기 때문에 HashSet에는 동일한 데이터로 인식해서

하나만 들어갈 필요가 있죠.

 

그런데 hashCode를 재정의하지 않으면 서로 다른 객체로 인식해서 각각의 메모리에 user1과 user2를 저장할 거에요.

그럼 나중에 검색을 할 때 문제가 되겠죠?

 

다음으로 equals()를 재정의하지 않았다면?

HashSet에는 user1만 입력되었다고 생각해볼게요.

 

위에서 말한 것처럼 HashSet은 HashCode를 먼저 비교한 후 equals를 비교해요.

여기서 user1의 객체가 HashSet에 있는지 확인하고 싶은데

분명 HashCode는 같지만 equals에서 값이 같은지 비교하지 못하기 때문

검색을 못할 수 있어요.

 

그래서 HashSet, HashMap, HashTable처럼 hashCode()와 equals()를 같이 사용한다면

둘 중 하나만 재정의(Overriding)하지 마시고 둘 다 해주세요!

 

'Language > JAVA' 카테고리의 다른 글

[JAVA] UUID  (0) 2022.05.25
Scanner vs BufferedReader  (0) 2022.05.11
0. JAVA란?  (0) 2020.01.08