정렬2 - Comparable, Comparator

2024. 6. 23. 16:25김영한 Java/컬렉션 프레임워크

자바가 기본으로 제공하는 Integer, String 같은 개게를 제외하고 MyUser와 같이 직접 만든 객체를 정렬하려면 어떻게 해야 할까? 내가 만든 객체이기 때문에 정렬을 할 때 내가 만든 두 객체 중에 어떤 객체가 더 큰지 알려줄 방법이 있어야 한다.

이때는 Comparable 인터페이스를 구현하면 된다. 이 인터페이스는 이름 그대로 비교 가능한, 비교할 수 있는 이라는 뜻으로, 객체에 비교 기능을 추가해준다.

 

public interface Comparable<T>{
	public int compareTo(T o);
}
  • 자기 자신과 인수로 넘어온 객체를 비교해서 반환하면 된다.
    • 현재 객체가 인수로 주어진 객체보다 더 작으면 음수
    • 두 객체의 크기가 같으면 0
    • 현재 객체가 인수로 주어진 객체보다 더 크면 양수

 

package collection.compare;

public class MyUser implements Comparable<MyUser>{
    private String id;
    private int age;

    

    public MyUser(String id, int age) {
        this.id = id;
        this.age = age;
    }

    public String getId() {
        return id;
    }

    public int getAge() {
        return age;
    }
    @Override
    public int compareTo(MyUser o) {
        System.out.println(this + " vs " + o);
        return this.age < o.age ? -1 : (this.age == o.age? 0 : 1);
    }

    @Override
    public String toString() {
        return "MyUser{" +
                "id='" + id + '\'' +
                ", age=" + age +
                '}';
    }
    
}
  • MyUser가 Comparable 인터페이스를 구현한 것을 확인할 수 있다.
  • compareTo() 구현을 보면 여기서는 정렬의 기준을 나이(age)로 정했다.
  • MyUser 클래스의 기본 정렬 방식을 나이 오름차순으로 정한 것이다.
  • Comparable을 통해 구현한 순서를 자연 순서(Natural Ordering)라 한다.
package collection.compare;

import java.util.Arrays;

public class SortMain3 {
    public static void main(String[] args) {
        MyUser myUser1 = new MyUser("a",30);
        MyUser myUser2 = new MyUser("b",20);
        MyUser myUser3 = new MyUser("c",10);

        MyUser[] array = {myUser1,myUser2,myUser3};
        System.out.println("기본 데이터");
        System.out.println(Arrays.toString(array));

        System.out.println("Comparable 기본 정렬");
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

 

실행 결과

기본 데이터
[MyUser{id='a', age=30}, MyUser{id='b', age=20}, MyUser{id='c', age=10}]
Comparable 기본 정렬
MyUser{id='b', age=20} vs MyUser{id='a', age=30}
MyUser{id='c', age=10} vs MyUser{id='b', age=20}
[MyUser{id='c', age=10}, MyUser{id='b', age=20}, MyUser{id='a', age=30}]

 

Arrays.sort(array)

기본 정렬을 시도한다. 이때는 객체가 스스로 가지고 있는 Comparable 인터페이스를 사용해서 비교한다.

MyUser가 구현한 대로 나이(age) 오름차순으로 정렬된 확인할 수 있다 .MyUser의 자연적인 순서를 사용했다.

 

다른 방식으로 정렬

만약 객체가 가지고 있는 Comparable 기본 정렬이 아니라 다른 정렬을 사용하고 싶다면 어떻게 해야할까?

나이가 아니라 아이디로 비교하는 예제를 추가로 만들어보자.

 

아이디로 비교할 수 있는 IdComparator를 하나 만들자.

package collection.compare;

import java.util.Comparator;

public class IdComparator implements Comparator<MyUser> {
    @Override
    public int compare(MyUser o1, MyUser o2) {
        return o1.getId().compareTo(o2.getId());
    }
}
package collection.compare;

import java.util.Arrays;

public class SortMain3 {
    public static void main(String[] args) {
        MyUser myUser1 = new MyUser("a",30);
        MyUser myUser2 = new MyUser("b",20);
        MyUser myUser3 = new MyUser("c",10);

        MyUser[] array = {myUser1,myUser2,myUser3};
        System.out.println("기본 데이터");
        System.out.println(Arrays.toString(array));

        System.out.println("Comparable 기본 정렬");
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));

        //추가
        System.out.println("IdComparator 정렬");
        Arrays.sort(array,new IdComparator());
        System.out.println(Arrays.toString(array));

        System.out.println("IdComparator.reversed()");
        Arrays.sort(array,new IdComparator().reversed());
        System.out.println(Arrays.toString(array));
    }
}

 

실행 결과

IdComparator 정렬
[MyUser{id='a', age=30}, MyUser{id='b', age=20}, MyUser{id='c', age=10}]
IdComparator.reversed()
[MyUser{id='c', age=10}, MyUser{id='b', age=20}, MyUser{id='a', age=30}]

 

Arrays.sort(array, Comparator)

기본 정렬이 아니라 정렬 방식을 지정하고 싶다면 Arrays.sort의 인수로 비교자(Comparator)를 만들어서 넘겨주면 된다.

이렇게 비교자를 따로 전달하면 객체가 기본으로 가지고 있는 Comparable을 무시하고, 별도로 전달한 비교자를 사용해서 정렬한다.

 

여기서는 기본으로 나이를 기준으로 정렬하지만, 아이디로 정렬하고 싶다면 IdComparator를 넘겨주면 된다.

결과를 보면 아이디(id)순으로 정렬된 것을 확인할 수 있다.

 

주의!

만약 Comparable도 구현하지 않고, Comparator도 제공하지 않으면 런타임 오류가 발생한다.

java.lang.ClassCastException: class collection.compare.MyUser cannot be cast to 
class java.lang.Comparable

Comparator가 없으니, 객체가 가지고 있는 기본 정렬을 사용해야 한다. 이때 Comparable을 사용한다. 그런데 Comparable을 찾는데 없으니 예외가 발생한다.

 

 

Comparable, Comparator 정리

객체의 기본 정렬 방법은 객체에 Comparable를 구현해서 정의한다. 이렇게 하면 객체는 이름 그대로 비교할 수 있는 객체가 되고 기본 정렬 방법을 가진다. 그런데 기본 정렬 외에 다른 정렬 방법을 사용해야 하는 경우 비교자(Comparator)를 별도로 구현해서 정렬 메서드에 전달하면 된다. 이 경우 전달한 Comparator가 항상 우선권을 가진다.

자바가 제공하는 Integer, String 같은 기본 객체들은 대부분 Comparable을 구현해 두었다.