고코딩

[JAVA] String에서의 ==와 equals() 차이점 본문

JAVA

[JAVA] String에서의 ==와 equals() 차이점

고코딩 2021. 1. 12. 10:02

자바의 최상위 클래스는 Object클래스이다. Object클래스에는 기본적으로 equals(), hashCode(), toString(), clone(), notify()등 여러 메소드가 존재한다. 그 중에서도 우리가 가장 많이 사용하는 equals()에 대해서 알아보자.

==연산자

처음 코딩을 배울때 '같다, 동등하다' 의 의미로 쓰는 것이 ==라고 배웠다 그래서 if(10 == 10)으로 작성해 if문을 실행하였다. 그때는 그저 같으면 됬지 라는 생각으로 사용하고 있었다. 그런데 어느 순간 String을 배우고 나니 equals()가 뿅 하고 나타났다. 그러고는 String은 객체여서 equals()로 비교해야 한다. 원래 그렇다 라고 밖에 설명을 듣지 못했다. 나는 그저 '아 문자열 내용 비교에서는 equals()를 사용하면 되는 구나'라고 이해하고 사용해 왔다. 그러다 StringBuffer 를 사요하면서 부터 문제가 생겼다. StringBuffer에는 equals가 먹히지 않는 것이였다. 무엇이 다른가 알아보기 위해 ==먼저 알아보았다.

단순하게 말하면 == 연산자는 int,boolean과 같은 primitive type에 대해서는 값을 비교한다. reference type에 대해서는 주소값을 비교한다. 사실 primitive type도 Constant Pool에 있는 특정 상수를 참조하는 것이기 때문에 결국 주소값을 비교하는 것으로 볼 수 있다. 같은 상수를 참조하면 주소값이 같으니 결국 같은 값이면 동일하다고 판단할 수 있다.

//primitive type
int a = 10;
int b = 10;
int c = 20;

System.out.println(a==b);    //true
System.out.println(a==c);    //false

a와 b는 값이 10으로 같으므로 true를 리턴하고 a와 c는 값이 다르므로 false를 리턴하게 된다. primitive type에서는 equals() 연산이 존재하지 않는다. 변수 a, b, c는 값이 stack에 존재하므로 stack에 있는 변수값만 비교하게 된다.

equals()

일단 알아둬야 할 것이 equals()는 최상위 클래스인 Object에 포함되어 있기 때문에 모든 하위 클래스에서 재정의 해서 사용이 가능하다. Object클래스의 equals()의 본내용을 살펴보면

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

위와 같이 정의 되어 있다. equals()도 내부적으로는 == 연산자를 사용해 주소를 비교하는 것과 다름이 없다. 하지만 우리가 String에서 사용하던 equals()는 내용이 주소를 비교하는 것이 아닌 내용을 비교하는 것이였다. 그럼 String클래스의 equals()를 봐보자.

 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;
    }

먼저 equals()변수로 들어온 객체가 자기 자신과 주소값이 같으면if (this == anObject)(이부분) true를 리턴한다. 주소 값이 다르다면 String 객체의 문자열을 Char 타입으로 하나씩 비교해 보면서 끝까지 같다면 true를 리턴 다르다면 false를 리턴한다.

String클래스에는 처음부터 equals()가 문자열을 비교하게 재정의 되어 만들어져있기 때문에 문자열 비교가 가능했던 것이다.

//reference type
String text1 = "sample";
String text2 = text1;
String text3 = new String("sample");
String text4 = new String("sample");

System.out.println(text1 == text2);            //true
System.out.println(text1.equals(text2));    //true
System.out.println(text3 == text4);            //false
System.out.println(text3.equals(text4));    //true

실제로 사용해 보면 그 차이점을 알수 있을 것이다.
참고로 text1은 new 연산자가 아닌 = "" 로 만들었기 때문에 힙 영역안에 String Constant Pool에 (주소,값) 형태로 저장되어 진다. 그 이후 ""로 생성한 String객체는 먼저 String Constant Pool 안에 자신이 생성할 문자열과 같은 값이 존재하는지 찾아본다. 같은 값이 존재한다면 공간을 새로 만드는 것이 아니라 주소값을 미리 만들어진 String Constant Pool에 있는 값에 연결한다. 결국 같은 값을 가르키게 때문에 == 연산 , equals() 연산 둘다 true를 리턴하게 될 것이다.
가장 중요한 포인트는 ==는 주소값이 같은지 아닌지 비교하는 것이고, equals()연산도 내부적으로 주소값을 비교하지만 String클래스에서는 equals()를 재정의해 내용을 비교하게 되어있다.