들어가며
컬렉션의 아이템들을 정렬할 때 쓰이는 클래스, 인터페이스들에 대해 깊게 알지 못했던게 최근 코딩테스트 응시 중 밑천이 드러나버렸다. 이를 반성하는 의미로 정리하고자 포스팅하려한다.
Comparable
Comparable 인터페이스는 컬랙션의 정렬 조건을
정렬 하고자 하는 객체 내부에 정의 할 수 있게 해준다.
public static class Person implements Comparable<Person> {
public String name;
public Person(String name) {
this.name = name;
}
//직관적으로 하기위해 getter, setter 생략
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name); //name으로 오름 차순 정렬
}
}
위와 같이 객체 내부에 compareTo() 메서드를 오버라이딩하여 정렬 조건을 정의 해두고
아래와 같이 작성하면 정의한 정렬 조건에 의해 정렬이 수행된다.
Comparable 인터페이스의 추상메서드인 compareTo() 메서드는
메서드를 호출한 객체보다 인자로 넘어온 객체가 더 크면 음수를
같다면 0을, 작다면 양수를 반환한다. 이 반환 값은 정렬에 사용된다.
public void example() {
List<Person> list = Arrays.asList(new Person(“김철수”), new Person(“이영희”));
list.sort(null); //Comparable 인터페이스의 compareTo 메서드를 오버라이딩 하지 않으면 예외가 발생한다.
}
※ String, Integer 등의 기본 데이터 타입 클래스는 내부적으로 Comparable의 compareTo를 이미 구현하고 있어 별다른 처리가 필요 없이 정렬을 수행할 수 있다.
List
아래 List 인터페이스의 sort() 메서드는 Comparable, Comparator의 조합으로 정렬이 수행될 수 있다.
void sort(Comparator<? super E> c)
다음은 sort() 메서드의 특징이다.
- 인자로 Comparator 또는 null 을 넘길 수 있음.
- 인자로 null을 넘기면, Comparator를 대신할 정렬조건이 필요하므로 컬렉션의 제네릭 타입은 Comparable 인터페이스를 상속하여 compareTo 메서드를 오버라이딩 해야함. 그렇지 않으면 예외 발생.
- 컬렉션의 제네릭 타입이 Comparable 인터페이스도 상속했고, Comparator도 인자로 넘기게되면 Comparable로 정의된 정렬 조건은 무시됨.
Comparator
Comparator는 함수형 인터페이스로 아래와 같은 메서드를 가진다.
int compare(T o1, T o2) // 반환값(음수, 0, 양수)에 따라 정렬시킬 수 있다.
쓰임새에 대한 예제는 다음과 같다.
public void example() {
List<String> list = Arrays.asList(“김철수”, “이영희”);
list.sort((o1, o2) -> o1.compareTo(o2)); //오름차순 정렬
list.sort((o1, o2) -> o2.compareTo(o1)); //내림차순 정렬
}
더 간단하게 작성할 수 있도록 Comparator는 다음의 static 메서드를 지원한다.
public void example() {
list.sort(Comparator.naturalOrder()); //오름차순 정렬
list.sort(Comparator.reverseOrder()); //내림차순 정렬
}
숫자를 비교한다면 단순히 뺄셈을 해주며 다음과 같이 작성하면 된다.
(o1, o2) -> o1 - o2 //오름차순 정렬
(o1, o2) -> o2 - o1 //내림차순 정렬
OR
Comparator.naturalOrder() //오름차순 정렬
Comparator.reverseOrder() //내림차순 정렬
Comparator로 사용자가 생성한 클래스 타입 객체들의 정렬은 어떻게 처리할까?
public class Person {
public String name;
public Integer point;
public Person(String name, Integer point) {
this.name = name;
this.point = point;
}
//직관적으로 하기위해 getter, setter 생략
}
public void example() {
List<Person> list = Arrays.asList(new Person(“김철수”, 10), new Person(“이영희”, 5));
...
}
위와 같이 값이 존재할 때 아래와 같이 간단하게 정렬할 수 있다.
list.sort((o1, o2) -> o1.name.compareTo(o2.name)) // name으로 오름차순 정렬
list.sort(Comparator.comparing(o -> o.name)) // name으로 오름차순 정렬
list.sort((o1, o2) -> o2.name.compareTo(o1.name)) // name으로 내림차순 정렬
list.sort((o1, o2) -> o1.point - o2.point); // point으로 오름차순 정렬
list.sort(Comparator.comparingInt(o -> o.point)); // point으로 오름차순 정렬
list.sort((o1, o2) -> o2.point - o1.point); // point으로 내림차순 정렬
Collections
컬렉션 클래스들의 유틸클래스인 Collections도 아래와같이 정렬 메서드를 지원한다.
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
내부적으로 List의 sort() 메서드를 단순히 호출하고 끝내기 때문에 나머지 내용들은 앞서 얘기 했던 내용과 동일하기에 적지 않는다.
Stream
stream의 중간 연산자 중 정렬을 담당하는 두 메서드가 아래와 같이 존재한다.
Stream<T> sorted()
- 이 메서드를 호출하기위해서는 중간연산을 진행하는 컬렉션의 제네릭 타입은 Comparable 인터페이스의 compareTo 메서드를 오버라이딩하여 정렬 조건을 정의해야한다.
- 정의하지 않는다면 예외가 발생한다.
Stream<T> sorted(Comparator<? super T> comparator)
- 정렬 조건을 Comparator로 정의한다.
- Comparable 인터페이스의 compareTo 메서드를 오버라이딩 했어도 이는 무시된다.
'☕️ Java' 카테고리의 다른 글
Volatile (0) | 2021.05.18 |
---|---|
Thread Local 개념과 내부 구조 (0) | 2021.05.08 |
HashMap은 탐색시 어떻게 O(1)의 성능을 낼까? (0) | 2021.05.02 |
Java는 Call by reference를 지원할까? (0) | 2021.03.28 |