Java - 객체 비교 (==, equals, Comparable, Comparator)

JS · 24 Oct 2020

Java에서 다음과 같은 operator 또는 메소드로 객체를 비교할 수 있습니다.

  • ==, != 연산자
  • equals()
  • Objects.equlas()
  • Comparable
  • Comparator

==, != 연산자

==, != 연산자 동일한 객체인지 다른 객체인지 비교할 수 있습니다. 객체가 갖고 있는 value를 비교하는 것이 아니라, 객체가 저장되어있는 메모리 주소를 비교하는 것을 주의해야 합니다.

다음과 같이 Primitive type을 비교할 때는 value만 비교합니다.

int a = 10;
int b = 10;
int c = 11;

System.out.println(a == b);
System.out.println(a != b);
System.out.println(a == c);

Output:

true
false
false

하지만 Primitive type이 아닌 Object를 비교할 때는 value가 아닌 Object의 메모리 주소를 비교합니다.

예를 들어, 아래와 같이 두개의 Integer 객체를 생성하였습니다. 두 객체의 메모리 주소는 다르기 때문에 결과는 false가 됩니다.

Integer a = new Integer(10);
Integer b = new Integer(10);

System.out.println(a == b);

Output:

false

하지만 다음 코드의 결과는 true가 됩니다. 이유는 두 객체는 메모리의 주소가 동일하기 때문입니다.

Integer a = Integer.valueOf(10);
Integer b = Integer.valueOf(10);

System.out.println(a == b);

Output:

true

아래 코드는 Integer.javavalueOf(int i)입니다. 코드를 보시면 캐시되어있는 객체가 있을 때, 새로 생성하지 않고 이전에 만든 객체를 리턴하도록 구현되어있습니다. 이 때문에 위의 두 객체의 메모리 주소가 같은 값을 가리키게 되었습니다.

// Integer.java
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

따라서, 객체 비교에 ==를 사용하는 것은 예상치 못한 결과를 가져올 수 있습니다. 꼭 사용해야한다면 이런 부분에 주의를 기울여야 합니다.

equals()

equals()를 이용하면 객체의 value를 비교할 수 있습니다.

Integer a = new Integer(10);
Integer b = new Integer(10);

System.out.println(a.equals(b));

Output:

true

Integer.javaequals() 코드를 보면, 객체와 인자로 전달되는 객체의 value를 비교하도록 구현되어있습니다.

public final class Integer extends Number implements Comparable<Integer> {
  ....

  public boolean equals(Object obj) {
      if (obj instanceof Integer) {
          return value == ((Integer)obj).intValue();
      }
      return false;
  }
}

자신이 직접 정의한 클래스의 객체들을 비교하고 싶다면, 이런 식으로 equals()를 직접 구현해줘야 합니다.

Objects.equals()

Java는 Objects.equals()라는 메소드도 제공합니다.

객체의 equals()를 사용하는 것과 동일한 결과를 리턴합니다.

Integer a = new Integer(10);
Integer b = new Integer(11);

System.out.println(Objects.equals(a, b));

Output:

false

클래스의 equals()와 차이점은, Objects.java의 메소드를 보면 null check 코드가 있기 때문에 NullPointerException은 발생하지 않습니다.

// Objects.java
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

Comparable

다음과 같은 Comparable 인터페이스를 클래스에 implements하여 비교할 수 있습니다.

public interface Comparable<T> {
    public int compareTo(T o);
}

compareTo()는 비교하는 객체가 같거나, 크거나, 작거나를 비교하여 0, 음수, 양수를 리턴할 수 있습니다.

다음과 같이 String을 갖고 있는 Text라는 클래스를 정의하였습니다. Comparable를 implements하여 compareTo()라는 메소드를 오버라이딩하였습니다.

public class Text implements Comparable<Text> {
    private String mText;

    public Text(String text) {
        mText = text;
    }

    public String getText() {
        return mText;
    }

    @Override
    public int compareTo(Text right) {
        return mText.compareTo(right.getText());
    }
}

다음과 같이 사용할 수 있습니다.

Text text1 = new Text("aaa");
Text text2 = new Text("aaa");
System.out.println(text1.compareTo(text2));

Output:

0

Comparable은 객체를 Sorting할 때 사용됩니다.

Comparator

Comparator도 Comparable과 비슷합니다. 차이점은 Comparator는 인터페이스가 아니라 객체로 생성한다는 것입니다.

다음과 같이 객체를 정렬할 때 Comparator를 사용할 수 있습니다. sort()으로 인자로 전달되어 내부에서 객체가 같은지, 큰지, 작은지 비교하는데 사용합니다. 예제에서는 객체의 주소를 비교하는 것이 아닌, 문자열의 길이를 비교하였습니다.

List<String> strings = new ArrayList<>();
strings.add("This code is free software");
strings.add("you can redistribute it");
strings.add("under the terms of the GNU General Public License version 2 only");

// sorting
Collections.sort(strings, new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }
});

System.out.println();
for (String str : strings) {
    System.out.println(str);
}

Output:

you can redistribute it
This code is free software
under the terms of the GNU General Public License version 2 only
댓글을 보거나 쓰려면 이 버튼을 눌러주세요.
codechachaCopyright ©2019 codechacha