본문 바로가기
개발/자바, 코틀린

[Java] 비교 연산

by 카펀 2022. 10. 14.

Java에는 다양한 비교 연산 방법이 있습니다.

다른 언어처럼 == 연산자를 비교하는 방법에서부터, Object로부터 상속 받은 equals() 메소드,  Object를 상속 받은 Objects 클래스의 equals()를 이용하는 방법을 소개해 보고자 합니다.

 

목차

1. ==

2. String.equals()

3. Objects.equals()

1. ==

== 연산자는 '비교하려는 두 대상의 주소값'을 비교합니다.

Java에는 기본 자료형과 참조 자료형이 존재하는데요.

기본 자료형은 call by value 형태로, == 연산자를 통해 비교하는데 무리가 없습니다.

참조 자료형의 경우 call by reference의 형태로, 생성 시 메모리 내에 주소값이 부여되는데요.

== 는 이 메모리 주소를 비교합니다. 따라서 내용물이 완전히 같더라도, == 연산자로 비교할 경우 false를 받을 수 있습니다.

 

String을 예시로 들어 보겠습니다. String은 기본 자료형처럼 동작하긴 하지만, 참조 자료형입니다.

따라서 아래와 같은 예시를 들어 비교를 해 보겠습니다.

 

public void stringCompare() {
	
    String st1 = "hello";
    String st2 = "hello";
    
    if (st1 == st2) {
    	System.out.println("st1 and st2 are ==.");
    }
    else {
        System.out.println("st1 and st2 are !=.");
    }

    String st3 = new String("hello");
    String st4 = new String("hello");

    if (st3 == st4) {
        System.out.println("st3 and st4 are ==.");
    }
    else {
        System.out.println("st3 and st4 are !=.");
    }
}

st1, st2는 리터럴 (literal) 방식입니다.

리터럴 방식으로 생성된 문자열은 'String pool'이라는 영역 내에 생성되는데요. 리터럴 방식으로 생성된 String은 호출 시 내부적으로 intern()이라는 메소드가 호출되며, 이 메소드는 String pool 내부에 해당 문자열이 존재하는지 검색하고, 존재한다면 그 주소를 리턴합니다.

즉, st1과 st2는 공통적으로 String pool 내의 'hello'가 존재하는지 찾으며, 위의 경우 'hello'가 존재하므로 해당 문자열의 주소를 리턴합니다. 같은 문자열을 가리키므로 주소를 비교해도 true를 얻습니다.

 

st3, st4는 다른 참조 자료형처럼 생성자를 통해 초기화하는 방식입니다.

위의 리터럴 방식과는 다르게, st3과 st4는 각자 독립적인 메모리 영역을 점유하게 됩니다. 따라서 각각 다른 메모리 주소를 가지게 되고, == 연산자로 비교한다면 false를 얻게 됩니다.

실행 결과

2. String.equals()

그렇다면 위의 st3, st4와 같은 경우 어떻게 비교하면 될까요?

아마 개발자들이 주로 의도하는 내용은 두 문자열의 주소를 비교하는 것이 아니라, 두 문자열이 같은지 비교하는 역할입니다.

 

Java의 모든 클래스는 Object를 상속 받는데, Object는 기본 메소드로 equals()를 가지고 있습니다.

String 역시 이를 상속 받아, 두 문자열의 값을 비교하는데 사용할 수 있습니다.

String.equals()

equals()는 앞서 ==와 같은 연산자가 아니라 메소드입니다. 따라서 위처럼 정의를 살펴볼 수 있습니다.

문자열을 주어진 Object와 비교해서, Object가 null이 아니고, 앞의 문자열과 같은 구성을 가지는 String 객체라면 true를 리턴하고, 그렇지 않다면 false를 리턴한다고 합니다.

 

public void stringCompare2() {
	
    String st3 = new String("hello");
    String st4 = new String("hello");
    
    if (st3 == st4) {
    	System.out.println("st3 and st4 are ==.");
    }
    else {
    	System.out.println("st3 and st4 are !=.");
    }
    
    if (st3.equals(st4)) {
    	System.out.println("st3 and st4 are equals() true.");
    }
    else {
    	System.out.println("st3 and st4 are equals() false.");
    }
    
    String st5 = new String("hi");
    if (st3.equals(st5)) {
    	System.out.println("st3 and st5 are equals() true.");
    }
    else {
    	System.out.println("st3 and st5 are equals() false.");
    }
}

위처럼 비교해 보면, 어떻게 될까요?

앞서 예시에서 st3 == st4는 각자 독립된 메모리 주소를 가지므로 false를 리턴했지만, equals() 비교는 값을 비교하므로 true를 리턴할 것입니다.

반면 st5는 값이 다르므로 false를 리턴할 것입니다.

실행 결과

3. Objects.equals()

앞서 String.equals()를 비교할 때, 'Compares this string to the specified object.' 라고 되어 있었습니다.

즉 a.equals(b)를 한다면, String a를 Object b와 비교한다는 뜻인데, 여기서 'a는 이미 String이다'임을 전제하고 있음을 알 수 있습니다.

 

아래와 같은 경우에는 어떻게 될까요?

 

public void stringCompare3() {
	
    String st1 = new String("hello");
    String st2 = null;
    
    if (st1.equals(st2)) {
    	System.out.println("st1 and st2 are equals() true.");
    }
    else {
    	System.out.println("st1 and st2 are equals() false.");
    }
    
    if (st2.equals(st1)) {
    	System.out.println("st2 and st1 are equals() true.");
    }
    else {
    	System.out.println("st2 and st1 are equals() false.");
    }
}

st2를 null로 초기화 하였습니다.

실행 결과는 아래와 같습니다.

실행 결과

NPE가 발생해 버리고 마는데요.

실제 운영 중인 서비스에서 String을 비교할 일이 매우 자주 있는데, 위처럼 값이 null일 때도 종종 있습니다. 예를 들어 DB에서 조회해 온 결과를 비교하려는데 조회한 결과가 null일 수도 있지요.

그렇다고 비교할 때마다 try-catch로 NPE를 잡을 수도 없고요.

 

이를 해결하기 위한 방법으로 'Null-safe 비교법'이라고 합니다.

말 그대로 NPE가 발생하여 프로그램이 중단되는 것을 방지하는 비교 방법입니다.

 

제가 소개해 드릴 방법은 Objects.equals()를 사용하는 방법입니다.

물론 특정 문자열의 값이 일치하는지만 비교하고 싶다면 앞서 소개해 드린 String.equals()를 사용하여, "test".equals(st1) 와 같은 식으로 사용해도 됩니다.

하지만 모든 상황에서 위와 같은 방법을 사용할 수는 없습니다.

 

Objects는 java.util.Objects 패키지에 포함되며, Java 1.7부터 추가된 기능입니다 (일부 메소드는 1.8에서 추가되었습니다).

공식 문서를 통해 더 자세한 내용을 확인할 수 있습니다.

Objects.equals()

Objects의 두 파라미터 a, b에는 @Nullable이라는 어노테이션이 붙어 있습니다.

즉, 비교 대상이 null이어도 괜찮습니다.

 

public static void main(String[] args) {
	
    String st1 = new String("hello");
    String st2 = null;
    String st3 = new String("hello");
    
    if (Objects.equals(st1, st2)) {
    	System.out.println("st1 and st2 are Objects.equals() true.");
    }
    else {
    	System.out.println("st1 and st2 are Objects.equals() false.");
    }
    
    if (Objects.equals(st1, st3)) {
    	System.out.println("st1 and st3 are Objects.equals() true.");
    }
    else {
    	System.out.println("st1 and st3 are Objects.equals() false.");
    }
}

String.equals()를 이용했을 땐 st2가 주체가 되었을 때 NPE가 발생했지만, Objects를 이용할 땐 주체가 되는 String이 없습니다.

실행 결과는 아래와 같습니다.

null과 비교했을 때, 같지 않으므로 false가 리턴됩니다.

물론 값이 같은 두 String st1, st3을 비교하는 경우에는 true가 리턴됩니다.

댓글