반응형

[JAVA] 자바 Comparable과 Comparator 사용법


자바에서 ComparableComparator은 둘 다 인터페이스이며, 정렬을 위해서 사용한다.

인터페이스이므로 구현을 통해 사용해야 하며 기본형 비교가 아닌 객체 비교를 위해 만들어졌다.

 

결론부터 말하자면, Comparable객체 내부에 비교 기준을 부여하여 다른 객체와 비교를 하는 것이고,

Comparator객체 외부에서 비교할 두 객체를 비교하여 비교 기준으로써의 역할을 한다.

여기서 주목할 점은 정렬을 해주는 것이 아니라, 비교 기준을 제공해준다는 점이다. 직접적으로 정렬을 진행하는 부분은 유틸 클래스가 처리해준다.

 

배열의 정렬을 직접 구현해보았다면 두 요소를 비교할 때 비교 연산자 크기 비교를 통해 true, false의 결과로 Swap을 하여 정렬을 한다는 점을 알 수 있을 것이다. 객체 비교에서는 Comparable과 Comparator는 바로 이러한 true, false 같은 정렬 기준을 제공하고, 유틸 클래스가 Swap을 처리하는 코드라고 생각하면 된다.

 

일반적으로 자바에서는 기본형 타입의 비교는 비교 연산자(==,<=,>=,<,>)등을 통해 손쉽게 비교할 수 있다. 하지만 객체 내부의 어떤 field를 비교하고 싶을 때에 객체 자체를 기본형 타입처럼 비교 연산자를 사용하게 되면 객체의 참조값을 비교하기 때문에 올바른 비교 결과를 얻을 수 없다. 그러므로 객체 내부 특정 field를 이용하여 정렬하고자 할 때를 위해 ComparableComparator가 필요하게 되었다. field를 비교하게 되면 그 결과로 객체를 정렬을 할 수 있다.

 

 


Comparable과 Comparator를 사용하기 이전에 알아야 할 것

 

예를 들어 배열의 요소가 두 개인 3과 5 인 배열을 오름차순 한다고 가정해보자.

 

배열이 [3, 5] 순서로 정렬돼있다고 가정했을 때

두 값을 뺄셈 연산하면 그 결과가 -2이다. [ ex) 3 - 5= - 2 ] 그러므로 뒤 값이 큰 걸 알 수 있다. 오름차순 정렬이므로 그대로 둔다. 

 

배열이 [5, 3] 순서로 정렬돼있다고 가정했을 때

두 값을 뺄셈 연산하면 그 결과가 2이다. [ ex) 5 - 3 = 2 ] 그러므로  앞 값이 큰 걸 알 수 있다. 오름차순 정렬을 해야 하므로 두 수의 위치를 Swap 한다.

 

오름차순 정렬에서는 뺄셈 결과가 음수면 앞 값이 작으므로 정렬이 올바르다. 그 위치 그대로 둔다.

뺄셈 결과가 양수면 앞 값이 크므로 오름차순 정렬을 위해 Swap을 해줘야 함을 알 수 있다.

밑에서 사용할 compareTo 함수는 return이 양수면 Swap 시킨다.

곧 이 말은, 뺄셈의 결과가 어떤 수이냐에 따라 Swap을 할지 말지인 정렬 기준을 제공할 수 있다는 점이다.

 

 


Comparable 사용법

 

Comparable은 위에서 서술했듯, 인터페이스이므로 추상 메서드를 갖고 있다. 갖고 있는 추상 메서드는 compareTo 하나이다. Comparable의 사용법은 객체 내부에서 비교 기준을 제공하므로 비교할 객체에 Comparable을 구현받아 객체 내부에 Comparable의 메서드인 compareTo를 Overriding 하여 사용해야 한다.

 

사람의 정보(이름, 나이)가 들어있는 객체를 정의하고 나이 순서로 정렬하고자 할 때

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PersonComparable implements Comparable<PersonComparable> {
    String name;
    int age;
 
    PersonComparable(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public int compareTo(PersonComparable o) { // compareTo를 overring하여 내부 field인 age를 비교
        return this.age - o.age;
    }
}
cs

compareTo는 객체 내부(자신)와 외부에서 매개변수로 들어오는 다른 객체와의 나이를 뺄셈 하여 정렬 기준을 제공할 수 있다.

 

 


Comparator 사용법

 

Comparator가 갖고 있는 추상 메서드는 compare이며, compareComparable compareTo와는 다르게 매개변수를 두 개를 받는다. 그 이유는 compare는 자신과 다른 것을 비교하기 위한 목적이 아니라 두 객체를 외부에서 인자로 받아 비교하기 위함이다.

 

마찬가지로 사람의 정보(이름, 나이)가 들어있는 객체를 똑같이 정의하고 나이 순서로 정렬하고자 할 때

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PersonComparator{
    String name;
    int age;
 
    PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String toString() {
        return name+", age="+age;
    }
}
 
 
// 외부에서 인터페이스 Comparator 구현한 정렬기준인 Class (compare method)
class SortStandard implements Comparator<PersonComparator>{
    @Override
    public int compare(PersonComparator p1, PersonComparator p2) {
        return (p1.name).compareTo(p2.name);
    }
}
cs

Comparable방식은 구현한 객체에 Comparable을 직접 구현하여 내부에 compareTo 메서드를 정의하는 방식이다. 반면 Comparator은 외부에서 SortStandard라는 객체를 새로 정의하여 Comparator을 구현하였다. 이러한 이유는 Comparator는 외부에서 정렬 기준이 되므로 SortStandard 객체를 정렬 기준으로 사용할 것이기 때문이다.

 

이 부분을 아래 코드를 통해 본다면 이해가 갈 것이다.

 

 


 ComparableComparator의 비교

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
       //---------------------------------Comparable 사용-----------------------------------//
       ArrayList<PersonComparable> cble = new ArrayList<>();
        cble.add(new PersonComparable("A"100));
        cble.add(new PersonComparable("B"1));
        cble.add(new PersonComparable("C"40));
 
        System.out.println("comparable 정렬 전 "+ cble);
        Collections.sort(cble); // 객체 내부에 정렬기준 (Comparable - compareTo)
        System.out.println("comparable 정렬 후 "+ cble);
 
 
        //---------------------------------Comparator 사용------------------------------------//
        ArrayList<PersonComparator> ctor = new ArrayList<>();
        ctor.add(new PersonComparator("다가"100));
        ctor.add(new PersonComparator("가나"30));
        ctor.add(new PersonComparator("나나"50));
 
        System.out.println("comparator 정렬 전 "+ ctor);
        Collections.sort(ctor, new SortStandard()); // 외부에 정렬기준Class (Comparator - compare)
        System.out.println("comparator 정렬 후 "+ ctor);
cs

실질적으로 정렬을 해주는 유틸 Class인 Collentions.sort 메서드를 사용하여 정렬을 해보자.

사용하는 부분(8번, 19번 라인)을 보자.

 

Comparable 부분은 sort함수에 ArrayList만 인자로 넣어 정렬을 시킨다. Collections.sort(cble);

PersonComparable 객체는 내부에 정렬 기준인 compareTo가 있기 때문에 문제없이 정렬을 할 수 있다.

 

반면, Comparator 부분은 사용법이 다르다. Collections.sort(ctor, new SortStandard());

sort 함수의 인자로 ArrayList인 ctor 이외에도 추가로 new SortStandard()를 부여했다.

 

이전 코드에서 PersonComparator의 설계를 보면 비교 기준이 객체 내부에 존재하지 않는다. Collections.sort()로 정렬을 하려면 비교 기준이 있어야 하는데 PersonComparator는 내부에 객체의 비교 기준이 없으므로 sort() 메서드의 추가적인 인자로 정렬 기준인 new SortStandard()을 준 것이다.

 

이 부분이 바로 정렬 기준인 Comparator를 구현한 객체를 사용한 부분이다. ComparatorComparable과 다르게 외부에서 정렬 기준을 설정한다는 의미가 이 코드를 통해 알 수 있다. 

 

또한, ComparatorComparable의 역할은 결국 두 수를 비교하여 Swap을 할지 말지를 결정한다.

 


문자 비교는 어떻게 할까

 

1
2
3
4
5
      // 문자 비교
    @Override
    public int compareTo(PersonComparable o) { // 자기자신 객체와 비교할 다른 객체
        return this.name.compareTo(o.name);
    }
cs

문자열의 비교는 compareTo의 return부분을 String클래스의 compareTo 메서드의 결괏값을 정렬 기준으로 보내주어 객체 비교를 할 수 있다.

즉, compareTo(PersonComparable o)는 객체 비교를 위한 compareTo이고,

this.name.compareTo(o.name)은 String 문자 비교를 위한 compareTo이다.

 

반응형

+ Recent posts