[JAVA] Comparable 및 Comparator를 사용하여 정렬하기

Arrays나 Collections 기본적으로 정의된 정렬은 오름차순/사전순이다.

int[] arr = {5, 10, 1, -3, 6};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // [-3, 1, 5, 6, 10]

List<Integer> list = new ArrayList<>();
list.add(5);
list.add(10);
list.add(1);
list.add(-3);
list.add(6);
Collections.sort(list);
System.out.println(list.toString()); // [-3, 1, 5, 6, 10]

하지만 기존에 정의된 오름차순/사전순이 아닌 특정한 기준이 필요할 때가 있다.
이 때 Comparable, Comparator 인터페이스를 사용해 기준을 바꿀 수 있다.

Comparable

java.lang.Comparable에 정의되어 있으며 compareTo메서드를 가진 인터페이스이다.
java에서 제공되는 정렬이 가능한 클래스들은 모두 Comparable을 구현하고 있으며, 정렬 시에 이에 맞게 정렬된다.
정렬이 필요한 클래스에 implements해준다.
나의 경우엔 객체를 우선순위큐나 arraylist에 저장할 때 주로 사용했다.

compareTo

int compareTo(T o) 리턴값에 따라 결과가 바뀐다.

  • 음수 : 현재 객체 < 파라미터로 넘어온 객체
  • 0 : 현재 객체 == 파라미터로 넘어온 객체
  • 양수 : 현재 객체 > 파라미터로 넘어온 객체

음수 또는 0이면 객체의 자리가 그대로 유지되며, 양수인 경우에는 두 객체의 자리가 바뀐다.

public static void main(String[] args) {
  List<Pos> list = new ArrayList<>();
  list.add(new Pos(1, 3));
  list.add(new Pos(5, 2));
  list.add(new Pos(1, 5));
  list.add(new Pos(3, 3));
  list.add(new Pos(4, 1));
  Collections.sort(list);
  System.out.println(list.toString());
  // [Pos [x=1, y=5], Pos [x=1, y=3], Pos [x=3, y=3], Pos [x=4, y=1], Pos [x=5, y=2]]
}

static class Pos implements Comparable<Pos> {
  int x, y;

  public Pos(int x, int y) {
    super();
    this.x = x;
    this.y = y;
  }

  @Override
  public String toString() {
    return "Pos [x=" + x + ", y=" + y + "]";
  }

  @Override
  public int compareTo(Pos o) {
    // x는 오름차순, x가 같다면 y는 내림차순으로 정렬
    if (this.x != o.x) return this.x - o.x;
    else return o.y - this.y;
  }
}


Comparator

java.util.Comparator에 정의되었으며 compare 메서드를 가진 인터페이스이다.
Arrays.sortCollections.sort에서 Comparator를 인자로 받는다.
Comparator를 상속받은 클래스를 만들어 사용할 수도 있지만, 나는 주로 익명클래스로 구현했다.

compare

int compare(T o1, T o2) 리턴값에 따라 결과가 바뀐다.

  • 음수 : 첫 번째 파라미터로 넘어온 객체 < 두 번째 파라미터로 넘어온 객체
  • 0 : 첫 번째 파라미터로 넘어온 객체 == 두 번째 파라미터로 넘어온 객체
  • 양수 : 첫 번째 파라미터로 넘어온 객체 > 두 번째 파라미터로 넘어온 객체

음수 또는 0이면 객체의 자리가 그대로 유지되며, 양수인 경우에는 두 객체의 자리가 바뀐다.

int[][] arr = {{1, 3}, {5, 2}, {2, 5}, {3, 3}, {4, 1}};

Arrays.sort(arr, new Comparator<int[]>() {
  @Override
  public int compare(int[] o1, int[] o2) {
    // 0번째 원소를 기준으로 내림차순 정렬
    return o2[0] - o1[0];
    }
});
		
for (int i = 0; i < arr.length; i++) {
  System.out.println(Arrays.toString(arr[i]));			
}
/*
[5, 2]
[4, 1]
[3, 3]
[2, 5]
[1, 3]
*/


compareTo와 compare의 리턴값에 대하여

구현하는 방법은 다양하다.

  1. 위에 작성한 것처럼 계산값을 리턴하는 방법
    return this.x - o.x
  2. if문을 사용하여 명확하게 표시하는 방법
    if (this.x > o.x) return 1
  3. 래퍼클래스에 정의된 비교문을 사용하여 리턴하는 방법
    return Integer.compare(o1[x], o2[x])
    등이 있다.

리턴값이 따른 결과가 내림차순인지 오름차순인지 자주 헷갈린다.
오름차순으로 하고 싶으면 앞 - 뒤
내림차순으로 하고 싶으면 뒤 - 앞
으로 기억하기로 하자..
compareTo는 인자가 한 개니까 앞 == 현재 객체로 생각하고
compare는 인자가 두개니까 앞 == 첫번째 인자로 생각하는 것이다.

2번째 방법을 사용할 때도 마찬가지로
오름차순 : if (this.x > o.x) return 1
내림차순 : if (this.x <= o.x) return 1
이 될 것이다.
양수가 리턴될 때 값이 바뀐다는 걸 기억하면 된다.

태그:

카테고리:

업데이트: