백준 문제를 풀다 보면 정렬이 필요한 문제가 종종 나온다. 대부분의 경우 Arrays.sort()나 Collections.sort()를 호출하여 해결할 수 있지만 정렬에 추가적인 조건이 붙었거나, 사용자가 정의한 객체를 정렬하게 될 경우 추가적인 작업이 필요하다.
이를 위해 필요한 Comparable과 Comparator에 대해 알아보고, 활용하는 법을 배워보자.
Comparable
Comparable 인터페이스는 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있다.
int[] arr = {1, 3, 5, 7, 2, 4, 6};
Arrays.sort(arr); //arr이 {1, 2, 3, 4, 5, 6, 7}로 정렬
위 예제는 int형 배열을 정렬하는 코드다. Arrays.sort()를 호출하면 컴퓨터가 알아서 배열을 정렬해준다. 이것이 가능한 이유는 Integer클래스에 Comparable이 구현되어 있기 때문이다.
Comparable은 객체를 어떤 기준에 맞춰 정렬할 것인지를 해당 객체의 클래스에 구현해 두었다. 때문에 정렬이 가능한 클래스에는 모두 Comparable이 구현되어 있다.
사용자가 정의한 클래스를 정렬할 경우
import java.util.*;
class Animal {
private String name; //이름
private String species; //종
private int date; //등록일
Animal(String n, String s, int d) {
this.name = n;
this.species = s;
this.date = d;
}
}
class sort {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Animal("coco", "cat", 20200314));
animals.add(new Animal("momo", "dog", 20180617));
animals.add(new Animal("bori", "dog", 20220228));
animals.add(new Animal("tori", "cat", 20151030));
animals.add(new Animal("hodu", "dog", 20170621));
Collections.sort(animals);
}
}
Collections.sort()를 호출하였으나 컴파일 에러가 발생하면서 코드가 실행이 되지 않는다. Animal 클래스에 Comparable이 구현되어있지 않기 때문에 어떤 기준으로 이 객체를 정렬해야 하는지 알지 못해서 정렬이 이루어지지 못하는 것이다. 이를 해결하기 위해서는 Animal 클래스에 Compalable를 구현해줘야 한다.
Comparable 구현하기
public interface Comparable {
public int compareTo(Object o);
}
Comparable의 실제 소스는 위와 같다. Comparable 인터페이스를 구현하기 위해서는 compareTo(Object o) 메서드에 대한 이해가 필요하다.
compareTo(Object o) 메서드는 해당 객체(this)와 전달받은 객체(o)의 값을 비교하여 아래와 같은 값을 반환한다.
- 비교하는 두 객체가 같으면 : 0 리턴
- 해당 객체의 값 < 전달 받은 객체의 값 : 음수 리턴
- 해당 객체의 값 > 전달 받은 객체의 값 : 양수 리턴
이에 맞게 compareTo()를 구현해주어야 한다.
예제
class Animal implements Comparable{
/*생략*/
public int compareTo(Animal a) {
if(this.date > a.date) {
return 1;
}
else if(this.date < a.date) {
return -1;
}
else {
return 0;
}
}
}
Animal 클래스의 등록일에 따라 오름차순으로 객체들이 정렬되도록 compareTo() 메서드를 구현했다. Comparable 인터페이스의 compareTo() 메서드를 구현한 뒤 Collections.sort()를 호출하면 정상적으로 정렬이 이루어지는 것을 확인할 수 있다.
정렬 결과
정렬 전
이름:coco 종:cat 등록일:20200314
이름:momo 종:dog 등록일:20180617
이름:bori 종:dog 등록일:20220228
이름:tori 종:cat 등록일:20151030
이름:hodu 종:dog 등록일:20170621
정렬 후
이름:tori 종:cat 등록일:20151030
이름:hodu 종:dog 등록일:20170621
이름:momo 종:dog 등록일:20180617
이름:coco 종:cat 등록일:20200314
이름:bori 종:dog 등록일:20220228
Comparator
Comporable을 구현한 클래스들은 기본적으로 오름차순에 의해 객체를 정렬하도록 되어있다. 하지만 내림차순으로 정렬을 하고 싶다거나 다른 기준에 의해서 정렬되도록 하고 싶을 때 Comparator를 구현하여 정렬 기준을 바꿀 수 있다.
Comparator를 구현하기 위해서는 compare(Object o1, Object o2)를 오버라이딩하여 구현하여야 한다. compare() 역시 객체를 비교해서 음수, 0, 양수 중 하나를 반환하도록 구현해야 한다.
- 비교하는 두 객체가 같으면 : 0 리턴
- o1의 값 < o2의 값 : 음수 리턴
- o1의 값 > o2의 값 : 양수 리턴
예제
이번에는 Animal 클래스에 새로운 정렬 기준을 정의해보겠다.
1) 종을 사전순으로 오름차순 정렬
2) 종이 같으면 등록일 순으로 오름차순 정렬
이렇게 정렬 기준이 복잡한 경우 Comparator를 활용하여 해결할 수 있다.
Collections.sort(animals, new Comparator<Animal>() {
@Override
public int compare(Animal o1, Animal o2) {
if(o1.species.equals(o2.species)) { //종이 같으면
return o1.date - o2.date; //등록일 비교
}
else { //종이 다르면
return o1.species.compareTo(o2.species); //종 사전순 비교
}
}
});
종이 같을 경우 등록일을 비교한다. o1.date - o2.date를 반환 값으로 설정하면
- o1.date = o2.date : 0 반환
- o1.date > o2.date : 양수 반환
- o1.date < o2.date : 음수 반환
의 효과가 있기 때문에 같은 종 내에서 오름차순 정렬을 할 수 있다.
종이 다른 경우 사전순으로 비교한다. 이때, compareTo() 메서드를 활용하면
- o1.species의 순서가 o2.species보다 앞일 때 : 음수 반환
- o1.species의 순서가 o2.species보다 뒤일 때 : 양수 반환
을 자동으로 해주기 때문에 종을 사전순으로 오름차순 정렬할 수 있게 해 준다.
정렬 결과
정렬 전
이름:coco 종:cat 등록일:20200314
이름:momo 종:dog 등록일:20180617
이름:bori 종:dog 등록일:20220228
이름:tori 종:cat 등록일:20151030
이름:hodu 종:dog 등록일:20170621
정렬 후
이름:tori 종:cat 등록일:20151030
이름:coco 종:cat 등록일:20200314
이름:hodu 종:dog 등록일:20170621
이름:momo 종:dog 등록일:20180617
이름:bori 종:dog 등록일:20220228
관련 문제
Comparator를 활용하여 해결할 수 있는 문제를 골라봤다. 이 글을 통해 잘 이해하였다면 한번 풀어보면서 연습하는 것을 추천한다.
https://www.acmicpc.net/problem/1181
풀이 :
2022.08.01 - [🧑💻코딩 테스트/백준 (BOJ)] - [코딩 테스트/백준 알고리즘] 1181번 : 단어 정렬 (Java 풀이)
https://www.acmicpc.net/problem/11650
풀이 :
2022.08.01 - [🧑💻코딩 테스트/백준 (BOJ)] - [코딩 테스트/백준 알고리즘] 11650번 : 좌표 정렬하기 (Java 풀이)
총평
이렇게 정리하고 보니 좀 더 이해가 된 거 같아서 기분이 좋다.
'✏️Java 공부 > 기타 등등' 카테고리의 다른 글
[Java 공부/기타] Java 형 변환 정리 (0) | 2022.07.31 |
---|---|
[Java 공부/ 기타] 클래스 내에서 데이터에 제약조건 걸기 (3) | 2022.03.08 |
댓글