brunch

You can make anything
by writing

C.S.Lewis

by 강관우 Mar 13. 2016

자바의 Comparable, Comparator

객체 비교를 정의하기 위한 인터페이스

“dBcAaEC” 와 같은 알파벳 문자열을 aAbBcC와 같은 알파벳 순서로 정렬하는 코드를 작성하세요.

 위에 문제를 해결하기 위한 코드를 자바로 작성한다면, 어떻게 짜시겠나요? 아마도 정렬을 실시하는 코드를 작성하되, 그 안에서 문자들에 대한 비교 연산을 새로 정의하는 형태로 작성해야 될 것입니다. 위 문제의 핵심은 '비교 연산'을 구현하는 데에 있습니다. 그냥 단순히 문자열을 char 배열로 바꾸고 각 char형을 정렬하고자 한다면 비교하는데 쓰이는 값이 ASCII값이므로 자연율로 정렬을 해버립니다. ABCDabcd와 같은 형태가 되겠죠. 


이렇게 자연율(일반적인 정렬순서)이 아니라 사용자가 새롭게 정렬 순서를 정의하고 싶거나 오브젝트를 정렬할 때, 오브젝트 정렬에 사용될 값을 지정하는 역할을 하는 인터페이스가 바로 Comparable과 Comparator입니다.


 우선 이 두 인터페이스가 필요한 이유를 쉽게 이해하기 위해서 일반적인 String 배열의 정렬 코드를 확인해보겠습니다.


// 배열을 정렬하기 위해서 Collection 프레임워크의 Arrays 유틸을 사용합니다.

    String[] fruits = new String[] {"Pineapple", "Apple", "Orange", "Banana"};  

    Arrays.sort(fruits); 

    for(String fruit: fruits){ 
        System.out.print(fruit + " "); 
    }

- 결과

Apple Banana Orange Pineapple

이 코드에서는 String의 기본 정렬 순서를 확인할 수 있습니다. 위 코드에서 사용된 Arrays의 sort 메소드는 아규먼트로 들어온 배열의 타입들이 참조 자료형일 경우, Comparable, Comparator 인터페이스를 구현하여 정렬 순서가 정의된 자료형만 정렬할 수 있습니다. 그러니까 Arrays.sort() 메소드로 정렬이 완료된 String은 참조 자료형이므로 내부적으로 이미 comparable 인터페이스를 구현한다는 사실을 짐작할 수 있습니다.


그렇다면 일반적인 오브젝트는 어떨까요? 이를 확인하기 위해서 Fruit 클래스를 한번 정의해봅시다.

public class Fruit {
    private String name;
    private int price;
    public Fruit(String name, int price) {
        this.name = name; 
        this.price = price; 
    } 
    public String getName() { 
        return name; 
    } 
    public void setName(String name) { 
        this.name = name; 
    } 
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price; 
    }
}

 

그리고 이 Fruit들이 여러 개 있고, 이 친구들을 정렬하기 위해서 다음과 같이 Arrays.sort 메소드를 사용한다면 어떻게 될까요? 


Fruit [] fruits = new Fruit [4];
fruits[0] = new Fruit("Pineapple", 70);
fruits[1] = new Fruit("Apple", 30);
fruits[2] = new Fruit("Orange", 35);
fruits[3] = new Fruit("Banana", 50); 

Arrays.sort(fruits);

for(String fruit: fruits){ 
    System.out.print(fruit + " "); 
}


그럴듯해 보입니다. 하지만 Arrays.sort()는 작동할까요? 당연히 위 코드는 동작하지 않습니다. 우리는 Arrays가 Fruit의 어느 속성을 참고하여 정렬할 것인지 정의해두지 않았으므로 아래와 같은 에러를 보게 될 것입니다.


Exception in thread "main" java.lang.ClassCastException: com.mkyong.common.Fruit cannot be cast to java.lang.Comparable at java.util.Arrays.mergeSort(Unknown Source) at java.util.Arrays.sort(Unknown Source) 


우리가 의도했던 바와 같이 오브젝트의 특정 속성을 가지고 정렬이 되게끔 하려면 Comparable 인터페이스를 구현하고 compareTo 메소드를 오버라이딩해야합니다.


public class Fruit implements Comparable{
    private String name;
    private int price;
    .
    .
    .
    public void setPrice(int price) {
        this.price = price; 
    }
    
    @Override
    public int compareTo(Fruit compareFruit) {
        int comparePrice = ((Fruit) compareFruit).getPrice();
    
        //ascending order
        return this.price - comparePrice;

        //descending order
        //return comparePrice - this.price; 
    } 
}


 새롭게 정의한 Fruit 클래스는 Comparable 인터페이스를 구현하고, compareTo() 메소드를 오버 라이딩하였으므로, Arrays는 이 정보를 이용해서 정렬 메소드를 수행할 수 있습니다. 다시 Fruit 정렬 로직을 수행하면 다음과 같은 결과를 얻을 수 있습니다.


- 결과

Apple Orange Banana Pineapple


 이렇게 해서 Fruit의 Price값을 기준으로 정렬하는 것이 가능해졌습니다. 그런데 만약 Fruit의 name을 가지고 정렬을 해야 하는 상황이면 어떻게 해야 할까요? 아니면 Price에 특정한 할인율을 적용해서 정렬을 해야 한다면? 그럴 때마다 Fruit 클래스 내부의 compareTo 메소드를 새로 정의해야 할까요?


 바로 이런 경우 사용하는 인터페이스가 Comparator입니다. Comparator 인터페이스는 compare 메소드를 정의하고 있습니다. compare 메소드는 2개의 인자를 받고 사용자가 구현한 로직대로 대소 관계를 정의할 수 있죠. 아래는 익명 내부 클래스 형태로 구현한 Comparator입니다.


Arrays.sort(fruits, new Comparator(){
    @Override
    public int compare(Fruit fruit1, Fruit fruit2) {            
        String name1 = fruit1.getName().toUpperCase();       
        String name2 = fruit2.getName().toUpperCase();
        //ascending order
        return name1.compareTo(name2);      
});

-결과

Apple Banana Orange Pineapple

위와 같이 Arrays.sort의 두 번째 인자는 Comparator 타입을 받아서 첫 번째 배열 정렬 순서를 재정의 할 수 있습니다. 이렇게 Comparable과 Comparator는 상호 보완적인 관계가 됩니다 :D


Collection 프레임워크에 포함된 Arrays나 Collections 클래스는 자료구조를 쉽게 다루기 위한 로직들을 제공해줍니다. 이를 잘 이용하는 것이 자바 프로그래밍의 생산성을 높이는데 큰 도움이 될 것입니다.


그래서 이는 꼭 익혀야 되는 개념이고, 이 글 처음에 소개된 질문은 Comparable과 Comparator의 개념을 학습하기에 좋은 퀴즈입니다.


“dBcAaEC” 와 같은 알파벳 문자열을 aAbBcC와 같은 알파벳 순서로 정렬하는 코드를 작성하세요.


매거진의 이전글 자바의 추상 클래스와 인터페이스
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari