본문 바로가기
Java

Comparable & Comparator 인터페이스 사용하기!

by GGShin 2022. 6. 4.

안녕하세요!

이번에는 컬렉션 내부의 요소들의 정렬 기준을 정의할 때 사용되는 Comparable과 Comparator에 대해 알아보려고 합니다.

이 두 interface는 이름도 기능도 비슷한데 쓰임에는 약간의 차이가 있습니다.

Comparable Comparator
1. 한 가지 기준으로 분류할 때 사용 1. 다수의 분류 기준이 필요 할 때 사용
2. compareTo() method 제공 2. compare() method, equals() method 제공
3. class에 영향을 줌
(정렬이 필요한 class에 implements)
3. class에 영향을 주지 않음
(별도로 class를 만들거나, 익명 클래스로 사용가능)

 

이 두 가지 interface를 사용해서 정렬 방법을 원하는 대로 정의할 수가 있고,

실제적인 사용은 Collections.sort() method를 통해 이루어집니다!

 

유사한 두개의 interface가 존재하는 데는 이유가 있겠죠. Comparator는 Comparable 보다 사용이 비교적 유연합니다.

 

Q. 그렇다면 언제 comparator를 사용하면 될까요? 🐣

 

A1) 만약에 우리가 3rd party library를 사용할 때 또는 소스 코드를 변경할 수 없는 상황이라면 Comparable을 implements할 수 없게 됩니다. 이런 상황에서 sort를 해야하는 경우에 해당 클래스가 아닌 외부에서 적용할 수 있는 Comparator가 유용하게 사용됩니다.

 

A2) 이미 Comparable interface를 implements하여 특정 field 값을 기준으로 하는 sort방법이 정의되어 있으나 다른 field 값을 이용한 sort 방법을 만들고 싶을 때도 Comparator가 사용됩니다. Comparator는 여러개 만들 수 있어서, 다양한 방식의 sort 방법을 만들어 낼 수 있다는 장점이 있습니다. 

 

이해를 돕기 위해 예시를 통해 살펴보겠습니다.

예시를 보고 다시 위의 설명을 읽어보신다면 더욱 이해가 잘 되실겁니다!

 

맛있는 쿠키의 정보가 담긴 쿠키 클래스를 만들어서 사용해볼게요 😋

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
public class Cookie {
 
    private String name;
    private int price;
    private int size;
 
    public Cookie(String name, int price, int size, int rank) {
        this.name = name;
        this.price = price;
        this.size = size;
        this.rank = rank;
    }
 
    public String getName() {
        return this.name;
    }
    public int getPrice() {
        return this.price;
    }
    public int getSize() {
        return this.size;
    }
 
}
 
 
cs

 

name, price, size 필드를 만들었고, 세가지 필드를 모두 initialize하는 constructor와 getter들을 만들어주었습니다.

그리고 Cookie를 담는 List 변수를 만들고,  그 안에 cookie instance들을 담아보겠습니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
 
Cookie a = new Cookie("a"200021);
Cookie c = new Cookie("c"130012);
Cookie d = new Cookie("d"150014);
Cookie b = new Cookie("b"9008);
 
List<Cookie> cookies = new ArrayList<>();
cookies.add(a);
cookies.add(c);
cookies.add(d);
cookies.add(b);
 
cs

 

이 상태에서 바로 Collections.sort(cookies)를 사용하면 어떻게 될까요?

 

 

이렇게 컴파일 에러가 발생합니다. 왜냐면 Cookie class에 Comparable를 implements 해주지 않았기 때문입니다.

그러면 가서 인터페이스를 구현해보겠습니다.


*추가 설명: String, Integer, Double등을 담고 있는 list에는 바로 Collections.sort(list)를 사용할 수 있는데,

그 이유는 이미 해당 클래스들이 Comparable<>을 implements하고 있기 때문입니다.

하지만 우리가 직접 만든 클래스들은 손수 Comparable을 implements해주어야 합니다.

 


1. Comparable 사용하기

 

사용법은 상당히 간단합니다. 비교가 이뤄져야 하는 class에 " implements Comparable<class명> " 이렇게 구현해주시면 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
public class Cookie implements Comparable<Cookie> {
 
    private String name;
    private int price;
    private int size;
 
    // ...
 
    @Override
    public int compareTo(Cookie cookie) {
        return 0;
    }
}
 
cs

 

그리고 override해야 하는 abstract method인 compareTo(Object o)를 override해주면 준비가 완료되었습니다!

이제 compareTo method 내용만 원하는대로 변경해주면 됩니다.

한 번 price를 기준으로 오름차순으로 정렬되게끔 해보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
 
    @Override
    public int compareTo(Cookie cookie) {
        if(this.getPrice() > cookie.getPrice()) {
            return 1;
        } else if(this.getPrice() < cookie.getPrice()) {
            return -1;
        } 
        return 0;
    }
 
cs

 

비교는 대상이 둘 이상이 있어야 하죠? compareTo에서는 instance 본인과 다른 instance를 비교하게 되어 있어서 parameter로는 다른 Cookie class instance 하나만 받게 되어있습니다. 

 

Line#4부터 보겠습니다. 자신의 가격이 비교 대상의 가격보다 크다면, 뒤에 위치해야겠죠? 순서상으로 뒤가 되도록 1을 반환하게 합니다.

반대로 자신의 가격이 비교 대상의 가격보다 작은 경우라면 앞에 위치해야 합니다. 앞으로 가도록 -1을 반환하게 합니다.

그리고 둘의 가격이 동일하다면 0을 반환해줍니다. 

 

이렇게 하면 리스트 안에서 둘 씩 비교해가며 -1을 반환한 instance가 앞에, 1을 반환한 instances는 뒤에, 0인 경우는 list에 순서를 바꾸지 않고 그대로. 이렇게 이동이 이루어집니다.

 

결과를 확인해보면, 

이제는 Collections.sort(cookies)가 에러 없이 동작하고,

price 기준 오름차순으로 잘 정렬된 것을 확인할 수 있습니다.

 

 

2. Comparator

 

그렇다면 price로 정렬하는 정렬방법은 그대로 유지하고 size로 정렬하는 방법도 만들어 두고 싶다면 어떻게하면 될까요?

앞에서 설명한 것 처럼 Comparator interface를 사용하면 됩니다.

사용 방법에는 두 가지가 있다고 했는데, 두 가지 방법 모두 살펴보겠습니다.

 

1) 익명 클래스 사용하기

익명 클래스로 Comparator를 사용하려면, 익명 클래스를 Cookie class 내부에 만들어 주면 됩니다.

다른 클래스에서 인스턴스 없이 사용할 수 있도록 static으로 선언해줍니다. (static final로 해주기도 하더라구요!)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Comparator;
 
public class Cookie implements Comparable<Cookie> {
 
    private String name;
    private int price;
    private int size;
    
    //...
 
    //Comparator
    static Comparator<Cookie> comparator = new Comparator<Cookie>(){
 
        @Override
        public int compare(Cookie o1, Cookie o2) {
            return 0;
        }
 
    };
 
}
 
cs

 

compare method 를 override한 다음 method의 내용을 원하는 대로 또 채워주면 끝입니다!

size field로 오름차순으로 정렬할 것이므로 아래처럼 작성해주면 됩니다.

 

1
2
3
4
5
6
7
8
9
     @Override
        public int compare(Cookie cookie1, Cookie cookie2) {
            if(cookie1.size > cookie2.size) {
                return 1;
            } else if (cookie1.size < cookie2.size) {
                return -1;
            }
            return 0;
        }
cs

 

price 오름차순으로 코드를 짜주었던 것과 동일한 로직으로 작성하였습니다. 의도한 대로 동작하는지 확인해보겠습니다.

 

 

원하는 대로 잘 정렬되었음을 확인할 수 있었습니다 🥳

 

2) 별도의 클래스에 구현해서 사용하기

 

Sort만을 위한 별도의 클래스를 만들고 그 클래스가 Coparator를 implements하도록 하면 됩니다.

그런다음 compare method를 override하고 메서드 코드를 작성해주는 것은 동일합니다.

 

이번에는 한 번 String type의 name field로 정렬해보았습니다.

String의 경우 앞에서 행한 숫자 type과는 다르게 >, < 연산을 할 수가 없습니다. 

대신 String class에 이미 구현되어있는 compareTo method를 사용하면 원하는 결과를 가져올 수 있습니다.

String의 compareTo method는 알파벳 순서로 정렬이 되게 됩니다!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
import java.util.Comparator;
 
public class SortByName implements Comparator<Cookie> {
 
    @Override
    public int compare(Cookie cookie1, Cookie cookie2) {
 
 
        //String type을 비교하기 위해서는 String에 이미 구현되어 있는 compareTo() method를 이용하면 됩니다!
        return cookie1.getName().compareTo(cookie2.getName());
 
    }
}
 
 
cs

 

사용할 때는 클래스 인스턴스를 comparator parameter에 넣어주면 사용이 가능합니다.

 

 

원하는 대로 알파벳 순서로 잘 정렬된 것을 확인할 수 있습니다.

 

3. 다수의 기준으로 분류하기

 

만약에 price 기준으로 정렬을 하되, 같은 price일 경우에는 알파벳 순으로 정렬하도록 하려면 어떻게 하면 될까요? 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
import java.util.Comparator;
 
public class MultipleSort implements Comparator<Cookie> {
 
    @Override
    public int compare(Cookie cookie1, Cookie cookie2) {
 
        int nameCompare = cookie1.getName().compareTo(cookie2.getName());
 
        int priceCompare = cookie1.getPrice().compareTo(cookie2.getPrice());
        //price 기준 비교 시 같다고 나온 경우라면 nameCompare가 적용되도록 합니다.
        return (priceCompare == 0) ? nameCompare : priceCompare;
    }
 
}
 
cs

 

위와 같이 int type의 nameCompare, priceCompare 두 가지 변수로 만들고 compare 후에 나온 값을 변수에 할당한 후에,

price로 비교하였을 때 0이 나오면 nameCompare가 적용되고 그 외의 경우는 priceCompare가 적용되도록 하였습니다.

(Geeks for Geeks의 내용을 참고하였는데, 이렇게 하려면 getPrice()의 return type을 Integer로 해주어야 되더라구요. 그래야 compareTo() method를 사용할 수가 있어서요. 이렇게 하지 않고 다른 방법이 있는지 또 알아보려고 합니다.)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
public class Main {
 
    public static void main(String[] args) {
 
        Cookie a = new Cookie("a"200021);
        Cookie c = new Cookie("c"130012);
        Cookie d = new Cookie("d"150014);
        Cookie b = new Cookie("b"9008);
        Cookie e = new Cookie("e"9005);
 
        List<Cookie> cookies = new ArrayList<>();
        cookies.add(a);
        cookies.add(e);
        cookies.add(c);
        cookies.add(d);
        cookies.add(b);
 
 
 
        //Comparator - multiple comparison
        Collections.sort(cookies, new MultipleSort());
        for(Cookie cookie : cookies) {
            System.out.println(cookie.getName() + " : " + cookie.getPrice());
        }
 
    }
}
cs

 

제대로 결과가 나오는지 확인하기 위해서 Cookie e = new Cookie("e", 900, 5) 를 추가해줬습니다. b와 동일 price인데 b보다 먼저 cookies list에 추가가 되어있을 때도 의도한대로 나오는지 보겠습니다.

 

 

원하던 대로 가격이 같은 경우에는 알파벳 순서대로 정렬되었음을 확인할 수 있었습니다! 👏

 

데이터를 잘 관리하고 사용하려니 정렬의 중요성이 더욱 크게 느껴지고 있습니다. 

앞으로도 필수적인 정렬방식에 대해 공부하고 포스팅 해보도록 하겠습니다. :) 감사합니다!


참고 자료

 

 

https://www.geeksforgeeks.org/comparator-interface-java/

 

Comparator Interface in Java with Examples - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

https://www.youtube.com/watch?v=oAp4GYprVHM 

 

반응형