Comparable
이 인터페이스를 구현하는 각 클래스 개체에 순서를 지정합니다. 이 순서를 natural ordering(자연순서) 라고 하며 클래스의 compareTo
메서드를 자연 비교 메서드라고 합니다.
이 인터페이스를 구현하는 객체 List 나 Array 는 (이하 List) Collections.sort()
로 자동으로 정렬할 수 있습니다. 따로 comparator 을 지정하지 않아도 말이죠.
compareTo 메소드
public int compareTo(T o);
Comparable 은 compareTo
라는 메서드를 가지고 있습니다.
이 객체를 지정된 객체와 비교하여 순서를 지정하는 메소드입니다. 파라미터 o 는 nullable 입니다.
compareTo
메서드를 호출하는 객체가 파라미터인 o 객체보다 더 클 때 양수, 호출하는 객체가 더 작을 때 음수, 같을 때 0을 리턴합니다.
만약 e1.compareTo(e2) == 0
와 e1.equals(e2)
이 같은 boolean 값을 같는다면 두 메서드는 동일합니다.
natural ordering 은 equals 와 같은 것이 좋습니다. 다르다면 sorted set(or map) 에서 이상하게 동작하기 때문입니다. Comparable 을 구현하는 모든 java core 클래스는 natural ordering 과 equals 가 같습니다. (java.math 는 예외임)
Comparable 을 구현하여 순서 지정
아래 코드로 순서를 지정하는 예를 들어봅시다.
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
if(this.age > o.age) return 1;
else if (this.age < o.age) return -1;
return 0;
// 이런 식으로도 사용할 수 있음.
// return this.age - o.age;
}
}
public class ComparableTest {
public static void main(String[] args) {
Student[] array = new Student[5];
array[0] = new Student("AA", 22);
array[1] = new Student("BB", 23);
array[2] = new Student("CC", 19);
array[3] = new Student("DD", 20);
array[4] = new Student("EE", 23);
Arrays.sort(array);
for (Student st : array) {
System.out.println("name: " + st.name + ", age: " +st.age);
}
}
}
Comparable
인터페이스를 구현한 후 compareTo()
메소드를 오버라이딩 해서 age
를 기준으로 오름차순 정렬하도록 한 코드입니다.
compareTo()
메서드의 코드 블록이 헷갈릴텐데
자바는 기본적으로 오름차순 정렬이며 compareTo()
메서드의 리턴값이 양수이면 오름차순, 음수이면 내림차순이라고 보면 됩니다.
———————— 출력 ————————
name: CC, age: 19
name: DD, age: 20
name: AA, age: 22
name: BB, age: 23
name: EE, age: 23
출력 결과를 보면 Student 기준으로 오름차순으로 잘 정렬이 된 것을 알 수 있습니다.
Comparator
일부 객체 컬렉션의 순서를 지정합니다. comparator
는 정렬 순서를 지정하여 sort
method 로 전달될 수 있습니다.
순서가 없는 데이터 구조(sorted map/set) 의 순서를 지정하거나 제어할 수 있습니다.
sorted map/set 을 정렬하기 위해 equals 와 다른 순서를 지정할 때는 주의해야 합니다. Comparable 와 마찬가지로 이상하게 동작할 수 있기 때문입니다.
참고로 일반적으로 Comparator 는 java.io.Serializable
을 같이 구현하는 것이 좋습니다. TreeSet 이나 TreeMap 같은 Serializable 한 데이터 구조가 성공적으로 직렬화되기 위해서는 필요합니다.
Comparable 과 달리 선택적으로 null 인수의 비교를 허용할 수 있습니다.
이 인터페이스는 Comparable 인터페이스와 다르게 꽤 많은 메소드를 가지고 있습니다. 여기서 가장 중요한 compare
메소드를 사용하면 클래스 단위로 정렬 기준을 정의하는 Comparable 과 달리 객체마다의 정렬 기준을 정의할 수 있습니다.
compare 메소드
int compare(T o1, T o2);
두 argument 의 순서를 비교합니다. o1 이 o2 보다 더 작으면 음수, 같으면 0, 더 크면 양수를 리턴합니다.
Comparator 을 사용하여 순서를 지정하는 예
String[] strArray = {"Apple", "Banana", "Tangerine", "Pear", "Blueberry"};
Arrays.sort(strArray, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1.length() > o2.length()) return -1;
else if (o1.length() < o2.length()) return 1;
return o1.compareTo(o2); // 문자열 길이가 같을 경우 알파벳 기준으로 오름차순 정렬.
}
});
for (String str : strArray) {
System.out.println(str);
}
sort 메소드의 두 번째 인자로 Comparator 익명 클래스를 구현해 넘겨주었습니다. Comparator 내부에 compare()
메소드를 오버라이딩 하여 정렬 기준을 작성했습니다.
그 결과 문자열의 길이가 긴 순서대로 정렬됩니다.
———————— 출력 ————————
Blueberry
Tangerine
Banana
Apple
Pear
Comparable 의 compareTo 보다 Comparator 의 compare 메소드가 더 헷갈리실텐데요. 아래와 같이 동작하는 것입니다.
o1 의 길이가 o2 보다 크면 -1(음수)을 리턴.
즉, o1 문자열의 길이가 더 크면 문자열의 길이 기준으로 내림차순으로 정렬한다는 것입니다.
compare 메소드를 람다 함수로 대체
위에 있는 코드 블록을 아래처럼 람다 함수로 바꿀 수 있습니다.
Arrays.sort(strArray, (o1, o2) -> {
if (o1.length() > o2.length()) return -1;
else if (o1.length() < o2.length()) return 1;
return o1.compareTo(o2);
});
Comparator는 패러미터가 위처럼 두 개인 메소드가 하나 뿐이기 때문에 이렇게 표현할 수 있습니다.
그 밖에 Comparator 는 자바 8버전부터 여러 다른 메소드들을 갖고 있습니다. 크게 어려운 부분은 없으니 궁금할 때 찾아보면 될 것 같습니다.
아래 글을 참고하여 작성하였습니다.
https://www.guru99.com/comparable-vs-comparator-java.html
https://yuja-kong.tistory.com/entry/Java-객체-정렬-Comparable-Comparator