[Java] 정렬(Comparable & Comparator)
Comparable & Comparator
- 둘 모두 인터페이스이다.
Comparable- 자신과 다른 객체를 비교 (비교의 대상)
- 숫자 클래스들,
boolean,String,Date,BigDecimal,BigInteger등
Comparator- 주어진 두 객체를 비교 (비교의 주체)
- 컬렉션에서는 정렬의 기준으로 사용
Arrays의 정렬 메서드,TreeSet이나TreeMap등의 생성자에 활용
차이점
Comparable
객체 내부에 기본 정렬 기준을 정의
Comparable 인터페이스는 해당 클래스 내부에서 기본 정렬 기준을 정의할 때 사용한다.
이 방식은 클래스가 스스로 어떻게 정렬될지를 알고 있도록 만드는 구조이다.
상품 객체를 가격 기준으로 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Product implements Comparable<Product> {
private String name;
private int price;
public Product(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public int getPrice() { return price; }
@Override
public int compareTo(Product other) {
return this.price - other.price; // 가격 기준 오름차순
}
@Override
public String toString() {
return name + ": " + price;
}
}
1
2
3
4
5
6
7
8
List<Product> list = Arrays.asList(
new Product("노트북", 1200),
new Product("마우스", 20),
new Product("모니터", 300)
);
Collections.sort(list);
System.out.println(list);
실행 결과
1
2
3
마우스: 20
모니터: 300
노트북: 1200
- 하나의 클래스에 하나의 기본 정렬 기준만 정의할 수 있다.
Collections.sort(list)또는Arrays.sort(array)처럼 간단하게 정렬 가능하다.
Comparator
외부에서 정렬 기준을 유연하게 지정
Comparator는 클래스 외부에서 정렬 기준을 정의하는 방식이다.
여러 정렬 방식이 필요하거나, 원본 클래스를 수정할 수 없는 경우에 적합하다.
이름 기준으로 정렬하는 Comparator 구현
1
2
3
4
5
6
public class NameComparator implements Comparator<Product> {
@Override
public int compare(Product p1, Product p2) {
return p1.getName().compareTo(p2.getName());
}
}
1
2
collections.sort(list, new NameComparator());
System.out.println(list);
실행 결과
1
2
3
노트북: 1200
마우스: 20
모니터: 300
람다 표현식도 가능
1
list.sort((p1, p2) -> Integer.compare(p2.getPrice(), p1.getPrice())); // 가격 기준 내림차순
1
list.sort((p1, p2) -> p1.getName().compareTo(p2.getName())); // 이름 기준 오름차순
Tree 구조에서의 사용법
컬렉션 프레임워크에는 정렬된 트리 구조를 제공하는 TreeSet과 TreeMap이 있다.
이들은 내부적으로 이진 탐색 트리(Red-Black Tree)를 사용하며, 저장되는 객체들이 정렬되어야 한다.
이때 정렬 기준을 제공하는 방식으로 Comparable 또는 Comparator 인터페이스를 사용하게 된다.
트리에 둘 모두 적용시
Comparator가 없으면Comparable의 기준을 따른다.
TreeSet에서 Comparable 사용
TreeSet은 삽입되는 객체가 Comparable을 구현하고 있어야 한다.
그렇지 않으면 ClassCastException이 발생한다.
가격 기준으로 정렬되는 상품 집합
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Product implements Comparable<Product> {
private String name;
private int price;
public Product(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public int getPrice() { return price; }
@Override
public int compareTo(Product other) {
return this.price - other.price;
}
@Override
public String toString() {
return name + ": " + price;
}
}
1
2
3
4
5
6
7
8
Set<Product> set = new TreeSet<>();
set.add(new Product("키보드", 30));
set.add(new Product("마우스", 20));
set.add(new Product("모니터", 200));
for (Product p : set) {
System.out.println(p);
}
출력 결과
1
2
3
마우스: 20
키보드: 30
모니터: 200
TreeSet에서 Comparator 사용
TreeSet 생성 시 Comparator를 전달하면, 객체가 Comparable을 구현하지 않아도 된다.
또는 기본 정렬 기준 대신 다른 기준으로 정렬할 수 있다.
이름 기준으로 정렬
1
2
3
4
5
6
7
8
9
// TreeSet 생성자에 Comparator를 전달
Set<Product> nameSortedSet = new TreeSet<>(Comparator.comparing(Product::getName));
nameSortedSet.add(new Product("키보드", 30));
nameSortedSet.add(new Product("마우스", 20));
nameSortedSet.add(new Product("모니터", 200));
for (Product p : nameSortedSet) {
System.out.println(p);
}
출력 결과
1
2
3
마우스: 20
모니터: 200
키보드: 30
TreeMap에서도 동일한 개념 적용
TreeMap은 키(Key)를 기준으로 정렬하며, 키 객체가 Comparable을 구현하거나 Comparator를 사용해야 한다.
사용자 이름을 키로, 포인트를 값으로 저장
1
2
3
4
5
6
7
8
Map<String, Integer> map = new TreeMap<>();
map.put("홍길동", 1000);
map.put("이몽룡", 800);
map.put("성춘향", 900);
for (String key : map.keySet()) {
System.out.println(key + " → " + map.get(key));
}
출력 결과 (이름 오름차순)
1
2
3
성춘향 → 900
이몽룡 → 800
홍길동 → 1000