[Java] Comparable & Comparator

조민서·2023년 3월 28일
2

JAVA

목록 보기
14/16

기본형 비교

int a = 2;
int b = 3;

if(a > b) {
    System.out.println("a가 b보다 큽니다.");
} else if(a == b) {
    System.out.println("a와 b는 같습니다.");
} else if(a < b) {
    System.out.println("a는 b보다 작습니다.");
}

primitive 타입의 실수 변수(byte, short, int, double 등등..)의 경우 부등호를 이용해 비교할 수 있다.

참조형 비교

public static class User {
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public static void referenceCompare() {
    User user1 = new User("조민서", 100);
    User user2 = new User("가오리", 40);
}

그럼 이 객체들은 어떻게 비교하지? 이름순? 나이순? 무엇을 비교 해야하지?
비교 하는 방법은 서비스마다 다르다.
즉, 이런 referecne 타입들을 비교하기 위해서 ComparableComparator가 존재한다.


Comparable & Comparator 특징

우선 둘다 인터페이스다.

그럼 뭐가 다를까?

Comparable

  • public int compareTo(T o)를 구현해야 한다.
  • compareTo 메서드 하나 밖에 없다.
  • (this 객체)와 (T o)를 비교 한다.

Comparator

  • int compare(T o1, T o2)를 구현해야 한다.
  • 함수형 인터페이스다.
  • 함수형 인터페이스지만 eqauls(오브젝트 메서드), 많은 default 메서드, static 메서드들이 구현되어 있다.
  • (T o1)과 (T o2)를 두 객체를 비교한다.

Comparable 사용법

깃허브 코드
위 코드를 보자 User에 Comparable를 구현체로 받아 compareTo를 구현했다.

출력 결과
가오리 40
조민서 100
나비 3000

그리고 User가 들어있는 list를 정렬하면 오름차순으로 정렬된다.
더 간단하게는 안될까? 된다!

@Override
public int compareTo(Object o) {
	User user = (User) o;
	return this.age - user.age;
}

이렇게 하면 오름차순으로 정렬된다. 근데 사실 이 방식은 좋지 않은 방식이다.
왜냐고? 오버플로우나 언더플로우가 발생할 수 있다.

그럼 어떡하라고?

@Override
public int compareTo(Object o) {
    User user = (User) o;
    return Integer.compare(this.age, user.age);
}

바로 Integer메서드의 compare()를 이용하면 된다.

나는 역순으로 정렬하고 싶은데? 된다!

@Override
public int compareTo(Object o) {
    User user = (User) o;
    return Integer.compare(user.age, this.age);
}

Comparator 사용법

깃허브 링크

User는 Comparator 구현체를 받아서 compare()메서드를 구현했다.
위 깃허브 코드를 보자. 위 사진에서 컴파일 에러가 발생하는 코드다.

우선 Collections.sort가 받는 형태를 보자.
1행은 List만
2행은 List, Comparator 구현체를 받는다.

분명 Comparable은 1행 형태의 sort()가 됐다. 차이점은 단 하나다.
Comparable은 this객체와 비교하지만, Comparator는 this객체와는 관련 없이 그냥 메서드에 들어오는 객체 2개를 비교한다.

Comparaor 용도

이 코드는 작동한다. 결과는 둘다 -1이다.
근데 빨간 밑줄을 보자. 이게 user1, user2 의미가 있나?

User user1 = new User("조민서", 100);
User user2 = new User("나비", 3000);
User comp = new User("", 0); // 비교용 객체
System.out.println(comp.compare(user1, user2));
System.out.println(comp.compare(user1, user2));

이런식으로 comp객체를 하나 만들어서 사용할 수 있다.

근데 그러면 아까 2행의 Compartor는 어떻게 이용하는거지?
익명함수, 람다식을 이용할 수 있다.

람다식

익명함수는 넘어가고 람다식만 설명하겠다. 간단해서 편하고 가독성도 좋다.

list.sort((User a, User b) -> Integer.compare(a.age, b.age));

Comparble, Comparator 인터페이스를 구현하지 않고 이렇게 즉석으로 사용할 수 있다.
앞에 User를 빼도 컴파일이 타입추론을 할 수 있지만, 나는 명시적으로 붙여줬는데 가독성이 더 좋은걸 택하면 된다.

아! 그리고 Collections.sort()가 아니라 list.sort()를 했는데 이게 뭐냐고?
list는 내가 선언한 List<User> list = new ArrayList<>();다.
자바 8이후 부터 List를 구현한 객체들은 default메서드로 sort가 있다. 아래사진을 참고하자.

코딩테스트 문제에 많이 나오는 우선순위큐에 적용 해보자.

우선순위큐 정렬

우선순위큐는 항상 정렬이 된다.
그럼 내부에서는 this객체를 통해 정렬이 되어야 한다. 그럼 Comparable, Comparator 무엇을 쓸까?
맞다! this객체를 비교하는 Comparable이다.

@Override
public int compareTo(Object o) {
    User user = (User) o;
    if(this.name == user.name) {
        return Integer.compare(this.age, user.age); // 이름이 같으면, 나이는 오름차순
    } else {
        return this.name.compareTo(user.name); // 이름이 다르면, 이름 오름차순
    }
}

User는 Comparable를 구현체로 받고 compareTo 코드를 다음처럼 구현했다.
1. 이름이 같으면, 나이를 오름차순으로 정렬
2. 이름이 다르면, 이름을 오름차순으로 정렬

출력 결과
가오리 1
가오리 4
조민서 2
조민서 3

원하는 결과가 맞게 나온다.

나는 Comparble로 구현했지만, 사실 Comparator로도 할 수 있다.
나중에 변수들이 많아져 복잡해지면 Comparator의 ComparingInt()등을 이용하면 더 쉽게 비교할 수 있다.

깃허브 코드

깃허브 전체 코드

profile
내 두뇌는 휘발성 메모리다. 😪

0개의 댓글