순수 자바 프로젝트로 카드게임을 프로그래밍 하던 중, 카드 출력 시 출력 순서에 대한 정렬 방식을 구현해야하는 기능구현 목록이 있었는데, 그 중 학습한 Comparable과 Comparator에 대해서 정리한 내용이다.
먼저 Comparator와, Comparable이 어떤건지 간단하게 먼저 알아보겠다.
Comparator
Comparator(비교자)는 정렬을 할 때 비교할 기준을 직접 제공하는 인터페이스이다. Comparator는 @FunctionalInterface이다.
그러므로 람다식이나 메서드 레퍼런스 방식으로 사용이 가능하며, String,Integer와 같은 기본 타입 같은 경우 그냥 호출해서 사용할 수 있다.
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
- 두 인수를 비교해서 결과 값을 반환하면 된다.
- 첫 번째 인수가 더 작으면 음수, 예(
-1
) - 두 값이 같으면
0
- 첫 번째 인수가 더 크면 양수, 예(
1
)
- 첫 번째 인수가 더 작으면 음수, 예(
자바의 기본 타입인 Integer와 String같은 객체들은 해당 메서드가 구현되어있다, 허나 이번에는 내가 프로그래밍을 하던 중 만난 정렬이 필요한 객체에 정렬 방식을 재정의 해야할 때는 어떻게 했는지 알아보자.
내가 만든 객체에 정렬 방식 정의하기
이때는 Comparable
인터페이스를 구현하면 된다.
이 인터페이스는 이름 그대로 비교 가능한, 비교할 수 있는 이라는 뜻으로, 객체에 비교 기능을 추가해 준다.
public interface Comparable<T> {
public int compareTo(T o);
}
- 자기 자신과 인수로 넘어온 객체를 비교해서 반환하면 된다.
- 현재 객체가 인수로 주어진 객체보다 더 작으면 음수, 예(
-1
) - 두 객체의 크기가 같으면
0
- 현재 객체가 인수로 주어진 객체보다 더 크면 양수, 예(
1
)
- 현재 객체가 인수로 주어진 객체보다 더 작으면 음수, 예(
public class Card implements Comparable<Card> {
// 생략 ....
@Override
public int compareTo(Card o) {
if (this.rank != o.rank) {
return Integer.compare(this.rank, o.rank);
}
return this.symbol.compareTo(o.symbol);
}
Card라는 객체의 속성으로는 rank(int)와 symbol(String) 두 가지의 속성이 존재한다.
첫 번째 정렬 조건은 숫자의 크기, 만약 숫자가 같다면 사전순으로 정렬하기로 설정했다.
결과
[5(♣), 6(♥), 8(), 10(♣), 13()], 합계 : 42
[3(♥), 4(♣), 4(), 11(♠), 11(♥)], 합계 : 33
플레이어1 승리!!
일단 나오는 Card들이 랜덤(Collections.shuffle() 사용)이라 rank가 같지만 문자가 다른 경우가 내가 정의한 순서 파악이 힘들어서 String 부분의 정렬이 내 예상대로 되었는지 확인하기 어려웠다.
그래서 정말 사전의 순서대로 정렬이 되었는지 확인하기 위해 Test 클래스를 만들어서 테스트 해보았다
public class StringSortingTest implements Comparable<StringSortingTest>{
private String alphabet;
public StringSortingTest(String alphabet) {
this.alphabet = alphabet;
}
@Override
public int compareTo(StringSortingTest o) {
return alphabet.compareTo(o.alphabet);
}
@Override
public String toString() {
return "StringSortingTest{" +
"alphabet='" + alphabet + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) {
StringSortingTest arrays = new StringSortingTest("Arrays");
StringSortingTest sorting = new StringSortingTest("Sorting");
StringSortingTest banana = new StringSortingTest("Banana");
List<StringSortingTest> test = Arrays.asList(arrays, sorting, sorting2);
test.sort(null);
System.out.println(test);
}
}
결과
[StringSortingTest{alphabet='Arrays'}, StringSortingTest{alphabet='Banana'}, StringSortingTest{alphabet='Sorting'}]
일단 3개의 단어를 가지고 사전 순으로 정렬이 되는지 테스트 해본결과 A 다음 B가 나온걸 볼 수 있었다.List<StringSortingTest> test = Arrays.asList(arrays, sorting, banana);
해당 코드 라인에서 확인할 수 있듯이 들어간 요소는 A -> S -> B 순이다.
Comparable, Comparator 정리
객체의 기본 정렬 방법은 객체에 Comparable
를 구현해서 정의한다.
이렇게 하면 객체는 이름 그대로 비교할 수 있는 객체가 되고 기본 정렬 방법을 가지는 객체가 된다.
그런데 기본 정렬 외에 다른 정렬 방법을 사용해야 하는 경우 비교자 ( Comparator
)를 별도로 구현해서 정렬 메서드에 전달하면 된다.
이 경우 전달한 Comparator
가 항상 우선권을 가진다. 자바가 제공하는 Integer
, String
같은 기본 객체들은 대부분 Comparable
을 구현해 두었다.
자료구조에서의 Comparable, Comparator
정렬은 배열뿐만 아니라 순서가 있는 자료구조에서 사용할 수 있다.
TreeSet
과 TreeMap
은 정렬된 상태를 유지하기 위해 Comparable
또는Comparator
를 사용한다.
HashSet
과 HashMap
과 같은 순서가 없는 자료구조에서는 요소를 정렬할 수 없지만, 정렬된 순서로 처리하려면 요소를 List
로 변환한 후 정렬 해야한다.
package problem.comparable;
import java.util.TreeSet;
public class SortTestMain3 {
public static void main(String[] args) {
MyMember a = new MyMember("A", 30);
MyMember a1 = new MyMember("B", 20);
MyMember a2 = new MyMember("C", 10);
TreeSet<MyMember> treeSet = new TreeSet<>();
treeSet.add(a);
treeSet.add(a1);
treeSet.add(a2);
System.out.println("Comparable 기본 정렬 나이 순" + treeSet);
System.out.println("Comparable 기본 정렬 나이 역순" + treeSet.reversed());
TreeSet<MyMember> idComparatorTreeSet = new TreeSet<>(new IdComparator());
idComparatorTreeSet.add(a);
idComparatorTreeSet.add(a1);
idComparatorTreeSet.add(a2);
System.out.println("idComparatorTreeSet 정렬 " + idComparatorTreeSet);
System.out.println("idComparatorTreeSet 정렬 역순" + idComparatorTreeSet.reversed());
}
}
이진 탐색 트리는 데이터를 저장할 때 왼쪽 노드에 저장해야 할 지, 오른쪽 노드에 저장해야 할 지 비교가 필요하다.
따라서 TreeSet
, TreeMap
은 Comparable
또는 Comparator
가 필수이다.
new TreeSet<new IdComparator>()
TreeSet
을 생성할 때 별도의 비교자를 제공하면 Comparable
대신 비교자( Comparator
)를 사용해서 정렬한다.
전체 정리
즉 자바의 기본타입인 Integer,String을 통한 정렬은 Comparator를 사용하고, 사용자 정의 객체 혹은 클래스를 사용한 정렬 방식을 정의하고 싶다면 Comparable을 사용하면 된다.
'Java > Collection' 카테고리의 다른 글
[Collection] Iterator와 Iterable (1) | 2024.06.04 |
---|---|
[Collection] Java의 Map (0) | 2024.05.29 |
[Collection] Java의 Set과 hashCode() (0) | 2024.05.29 |
[Collection] Java의 TreeSet의 특징 및 구조 (0) | 2024.05.29 |
[Collection] 자바의List 그리고 ArrayList,LinkedList 실제 성능 테스트 (0) | 2024.05.24 |