새소식

JAVA (개념, 알고리즘)

Comparator와 Comparable

  • -

(1) Comparator와 Comparable

Comparator와 Comparable은 모두 인터페이스로 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있다

 

Comparable을 구현하고 있는 클래스들은 같은 타입의 인스턴스끼리 서로 비교할 수 있는 클래스들,

주로 Integer와 같은 wrapper클래스와 String, Date, File과 같은 것들이며

기본적으로 오름차순, 즉 작은 값에서부터 큰 값의 순으로 정렬되도록 구현되어 있다.

그래서 Comparable을 구현한 클래스는 정렬이 가능하다는 것을 의미한다.

 

Comperator와 Comparable의 실제 소스는 다음과 같다.

public interface Comparator {
    int compare(Object o1, Object o2); // o1과 o2를 비교.
    boolean equals(Object obj);
}

public interface Comparable {
    int compareTo(Object o); // 객체 자신(this)과 o를 비교.
}

compareTo의 반환값은 int이지만 실제로는

비교하는 두 객체가 같으면 0,

비교하는 값보다 작으면 음수,

크면 양수를 반환하도록 구현해야 한다.

 

이와 마찬가지로 compare()도 객체를 비교해서 음수, 0, 양수 중 하나를 반환하도록 구현하여야 한다.

 

Comparable : 기본 정렬 기준을 구현하는데 사용.

Comparator : 기본 정렬 기준 외 다른 기준으로 정렬하고자 할 때 사용.

 

예제를 살펴보도록 하자.

import java.util.*;

public class Ex11_7 {
    public static void main(String[] args) {
        String[] strArr = {"cat", "Dog", "lion", "tiger"};

        Arrays.sort(strArr); // String의 Comparable구현에 의한 정렬
        System.out.println("strArr=" + Arrays.toString(strArr));

        Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); // 대소문자 구분안함
        System.out.println("strArr=" + Arrays.toString(strArr));

        Arrays.sort(strArr, new Descending()); // 역순 정렬
        System.out.println("strArr=" + Arrays.toString(strArr));
    }
}

class Descending implements Comparator {
    public int compare(Object o1, Object o2){
        if( o1 instanceof Comparable && o2 instanceof Comparable) {
            Comparable c1 = (Comparable)o1;
            Comparable c2 = (Comparable)o2;
            return c1.compareTo(c2) * -1 ; // -1을 곱해서 기본 정렬방식의 역으로 변경한다.
            // 또는 c2.compareTo(c1)와 같이 순서를 바꿔도 된다.
        }
        return -1;
    }
}

Arrays.sort()는 배열을 정렬할 때, Comparator를 지정해주지 않으면 저장하는 객체(Comparable을 구현한 클래스의 객체)에 구현된 내용에 따라 정렬된다.

 

// 객체 배열에 저장된 객체가 구현한 Comparable에 의한 정렬
static void sort(Object[] a) 

// 지정한 Comparator에 의한 정렬
static void sort(Object[] a, Comparator c)

String의 Comparable구현은 문자열이 사전 순으로 정렬되도록 작성되어 있다. 

(문자열의 오름차순 정렬은 유니코드의 순서가 작은 값에서부터 큰 값으로 정렬되는 것이다.)

 

public static final Comparator CASE_INSENSITIVE_ORDER

이 Comparator를 이용하면, 문자열을 대소문자 구분없이 정렬할 수 있다.

 

Integer에서의 Comparable

Integer클래스의 compareTo()는 두 Integer객체에 저장된 int값(value)을 비교한다.

같으면 0, 크면 -1, 작으면 1을 반환한다.

 

정리

Comparable을 구현한 클래스들이 기본적으로 오름차순으로 정렬되어 있지만, 

내림차순으로 정렬한다던가 아니면 다른 기준에 의해서 정렬되도록 하고 싶을 때

Comparator를 구현하여 정렬기준을 제공할 수 있다.

 

Integer와 Comparable의 예제를 살펴보도록 하자.

 

import java.util.*;

public class Ex11_8 {
    public static void main(String[] args) {
        Integer[] arr = { 30, 50, 10, 40, 20 };

        Arrays.sort(arr); // Integer가 가지고 있는 기본 정렬기준 compareTo()로 정렬
        System.out.println(Arrays.toString(arr));

        // sort(Object[] objArr, Comparator c)
        Arrays.sort(arr, new DescComp()); // DescComp에 구현된 정렬 기준으로 정렬
        System.out.println(Arrays.toString(arr));
    } // main
}

class DescComp implements Comparator {
    public int compare(Object o1, Object o2) {
        if(!(o1 instanceof Integer && o2 instanceof Integer))
            return -1; // Integer가 아니면, 비교하지 않고 -1 반환

        Integer i  = (Integer)o1;
        Integer i2 = (Integer)o2;

        // return i2 - i; 또는 return i2.compareTo(i);도 가능
        return i.compareTo(i2) * -1; // 기본 정렬인 compareTo()의 역순으로 정렬
    }
}

1~50의 숫자 중 25개를 골라서 5*5 빙고판을 만드는 예제이다.

next()는 반환값이 Object타입이므로 형변환해서 원래의 타입으로 되돌려 놓아야 한다.

 이를 몇번 실행해보면 같은 숫자가 비슷한 위치에 나온다는 사실을 발견할 수 있다.

이는 HashSet은 저장된 순서를 보장하지 않고 자체적인 저장방식에 따라 순서가 결정되기 때문이다.

따라서 이러한 경우에는 HashSet보다 LinkedHashSet이 더 나은 선택이다.

'JAVA (개념, 알고리즘)' 카테고리의 다른 글

2. 배열  (0) 2023.01.17
1. 문자열(String)  (0) 2023.01.14
오버라이딩과 오버로딩  (0) 2022.12.28
상속과 포함관계  (0) 2022.12.28
생성자  (0) 2022.12.22
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.