[Java] Comparable과 Comparator

3Beom's 개발 블로그·2022년 11월 8일

본 글은 우아한 테크코스 프리코스 1주차 미션 중 공부한 내용을 기록한 것이다.
-> 우아한 테크코스 프리코스 1주차 미션 java-onboarding
-> 필자가 제출한 코드
-> 1주차 미션 회고


Comparable, Comparator

  • Comparable과 Comparator는 객체를 비교할 수 있도록 도와주는 인터페이스이다.
  • 기본 변수 타입인 primitive 타입의 경우, 부등호를 통해 쉽게 비교가 가능하다.
  • 하지만 객체의 경우, 두 객체를 비교하기 위해서는 개발자가 기준을 정해주어야 한다. 이 때 Comparable 혹은 Comparator가 활용된다.
  • 객체 정렬 과정에서도 많이 활용된다.

< 정렬 과정에서 비교 규칙 >

  • Comparable, Comparator 설명에 앞서, 해당 인터페이스를 활용하여 객체를 정렬시킬 때 다음과 같이 두 가지 로직으로 구현되어질 수 있다.

<로직 1>

  • 오름차순

    • 앞 값 > 뒷 값 -> return 1; (혹은 양수 값)
    • 앞 값 < 뒷 값 -> return -1 : (혹은 음수 값)
    • 앞 값 == 뒷 값 -> return 0;
  • 내림차순

    • 오름차순의 반대로 해주면 된다.

    (Comparable의 경우, 앞 값이 자기 자신이 된다.)

<로직 2>

  • 오름차순
    • return 앞 값 - 뒷 값;
  • 내림차순
    • return (앖 값 - 뒷 값) * -1;
    • return 뒷 값 - 앞 값;

<유의할 점>

  • 로직 2의 경우, 자료형 범위를 이탈한 값이 들어올 경우에 원하는 결과가 나오지 않을 수 있다.

    • 예를 들어 int의 경우, 자료형의 범위는 (-2^31) ~ (2^31 - 1) 이다.

    • 해당 범위를 넘어선 값이 들어올 경우, Underflow 혹은 Overflow가 발생한다.

      • Undeflow : 범위의 최소값보다 작은 수가 들어오면 최대값 쪽으로 넘어가버리는 현상
      • Overflow : 범위의 최대값보다 큰 수가 들어오면 최소값 쪽으로 넘어가버리는 현상
    • 따라서 Underflow나 Overflow가 발생할 여지가 있는지에 대한 확인이 필요하다.

  • 로직 1처럼 부등호를 활용하여 대소 비교 후, (1, 0, -1) 중 하나를 반환하는 것이 안전하며, 권장되는 방식이다.

< Comparable >

  • java.lang 패키지에 있어 import 해 줄 필요가 없다.

  • 자기 자신매개변수 객체를 비교한다.

  • public int compareTo(T o) 메소드를 구현해야 한다.

    class ComparableExample implements Comparable<ComparableExample> {
      int field1;
      int field2;
      
      ...
      
      @Override
      public int compareTo(ComparableExample ce1) {
        if (this.field1 > ce1.field1) {
          return 1;
        } else if (this.field1 < ce1.field1) {
          return -1;
        } else {
          if (this.field2 > ce1.field2) {
            return 1;
          } else if (this.field2 < ce1.field2) {
            return -1;
          } else {
            return 0;
          }
        }
      }
      
      ...
      
    • 위 예시 코드의 경우, ComparableExample class의 객체를 비교하기 위해 compareTo() 메소드를 정의하였다.
      • field1 기준으로 먼저 비교 하고, field1이 같을 경우 field2를 기준으로 비교한다.
      • 이는 곧 정렬 과정에서 "field1 기준 오름차순, 동일한 field1 값을 갖는 객체들은 field2 기준 오름차순" 의 규칙이 될 수 있다.

< Comparator >

  • java.util 패키지에 있어 import가 필요하다.

  • 매개변수로 입력되는 두 개의 객체를 비교한다.

  • public int compare(T o1, T o2) 메소드를 구현해야 한다.

    class ComparatorExample implements Comparator<ComparatorExample> {
      int field1;
      ...
      @Override
      public int compare(ComparatorExample ce1, ComparatorExample ce2) {
        if (ce1.field1 > ce2.field2) {
          return 1;
        } else if (ce1.field1 < ce2.field2) {
          return -1;
        } else {
          return 0;
        }
      ...
    • 기본적으로 compareTo와 유사하지만, 파라미터로 두 개의 객체가 전달된다.
    • compareTo에서 '자기자신' 에 해당하는 객체가 첫번째 파라미터 객체(= ce1)이다.

< Comparator 익명 객체 활용 >

(익명 객체)

  • 이름이 정의되지 않은 객체

  • 구현

    부모클래스 이름 = new 부모클래스(매개변수) {
      구현부
    };
    
    Comparator<Type> comparator = new Comparator<Type>() {
      구현부
    }
  • Comparator는 인터페이스이다. 따라서 객체를 생성할 수 없다. 하지만 위 코드의 경우, 'comparator' 라는 이름의 객체가 생성된다.

  • 이는 익명 객체를 통해 구현된 것으로, 내부에서 Comparator 를 상속받는 클래스를 생성하고, 객체를 만들어 comparator에 넣어준 것이다.

  • '구현부' 에서는 Overriding 혹은 추가 기능들을 클래스 만들듯이 넣어주면 된다.

<Comparator 활용>

  • Comparator 익명 객체를 통해 비교 연산자 객체를 만들 수 있다.

    • 예시
    Comparator<ComparatorExample> comparator = new Comparator() {
      @Override
      public int compare(ComparatorExample ce1, ComparatorExample ce2) {
        if (ce1.field1 > ce2.field2) {
          return 1;
        } else if (ce1.field < ce2.field) {
          return -1;
        } else {
          return 0;
        }
      }
    };
    
    if (comparator.compare(comparatorExample1, comparatorExample2) == 1) {
      System.out.println("First one is bigger than second one.");
    }
  • 미션 Problem 7에서 다음과 같이 활용되었다.

    Collections.sort(sortedRecommendScore, new Comparator<Map.Entry<String, Integer>>() {
    			@Override
    			public int compare(Map.Entry<String, Integer> score1, Map.Entry<String, Integer> score2) {
    				int differenceOfTwoScores = (score1.getValue() - score2.getValue()) * -1;
    
    				if (differenceOfTwoScores == 0) {
    					return score1.getKey().compareTo(score2.getKey());
    				} else {
    					return differenceOfTwoScores;
    				}
    			}
    		});
    • Collections의 sort 메소드의 매개변수로 Comparator를 전달해주어야 하고, 이를 위해 익명 객체로 생성하여 전달한 것이다.

내용 출처

https://st-lab.tistory.com/243

profile
경험과 기록으로 성장하기

0개의 댓글