티스토리 뷰
자바에서는 리스트를 정렬할 때 기본형 데이터 타입에 대한 정렬 기준이 정의되어 있기 때문에 Collections.sort로 리스트를 정렬할 수 있습니다. 하지만, 기본형 데이터 타입이 아닌 커스텀 타입의 리스트를 정렬하려고 하면 컴파일 에러가 발생합니다.
순위에 따라 선수들을 정렬하는 축구팀을 예시로 설명해보겠습니다.
먼저 선수들에 대한 정보를 저장하기 위해 Player라는 클래스를 만들 수 있습니다.
public class Player {
private int ranking;
private String name;
private int age;
// constructor, getters, setters
}
Player 클래스를 생성한 후 선수들의 정보를 리스트에 넣고 Collections.sort를 이용해 정렬을 수행하기 위해 PlayerSorter라는 클래스도 만들어보겠습니다.
public static void main(String[] args) {
List<Player> footballTeam = new ArrayList<>();
Player player1 = new Player(59, "John", 20);
Player player2 = new Player(67, "Roger", 22);
Player player3 = new Player(45, "Steven", 24);
footballTeam.add(player1);
footballTeam.add(player2);
footballTeam.add(player3);
System.out.println("Before Sorting : " + footballTeam);
Collections.sort(footballTeam);
System.out.println("After Sorting : " + footballTeam);
}
PlayerSorter 클래스를 실행하고 Collections.sort를 수행할 때 정렬이 되지 않고 다음과 같은 에러 메세지를 보게 됩니다.
The method sort(List<T>) in the type Collections
is not applicable for the arguments (ArrayList<Player>)
클래스는 사용자 정의에 의해 생성된 데이터 타입이기 때문에 리스트를 정렬할 수 없게 되는 것입니다.
이런 상황에서 사용할 수 있는 인터페이스로 Comparable과 Comparator가 있습니다.
Comparable 인터페이스
Comparable 인터페이스는 동일한 타입을 가지는 객체와 또 다른 객체를 비교할 수 있는 전략을 정의합니다. 객체간의 일반적인 정렬을 하고자 할 때 사용하며, 자바에서 기본적으로 제공하는 Comparable 인터페이스를 상속받아 인터페이스 내 compareTo() 함수를 오버라이딩하여 정렬 방법에 대해 정의하게 됩니다.
Player 클래스가 Comparable 인터페이스를 상속받아 compareTo 함수를 오버라이딩 하여 선수들의 순위를 비교하는 정렬을 수행하는 코드는 다음과 같습니다.
public class Player implements Comparable<Player> {
// same as before
@Override
public int compareTo(Player otherPlayer) {
return Integer.compare(getRanking(), otherPlayer.getRanking());
}
}
정렬 순서는 compareTo() 함수가 리턴하는 값에 의해 결정되며 Integer.compare(x,y)에서 x가 y보다 작은 경우 -1을 리턴하고, x와 y가 같으면 0, x가 y보다 더 크면 1을 리턴하게 된다. 이 함수는 매개변수로 받은 객체와 비교했을 때 해당 객체가 더 작은지, 같은지, 큰지에 따라 숫자를 리턴하게 되며 현재 코드는 순위를 기준으로 오름차순으로 정렬하겠다는 의미가 됩니다.
public static void main(String[] args) {
List<Player> footballTeam = new ArrayList<>();
Player player1 = new Player(59, "John", 20);
Player player2 = new Player(67, "Roger", 22);
Player player3 = new Player(45, "Steven", 24);
footballTeam.add(player1);
footballTeam.add(player2);
footballTeam.add(player3);
System.out.println("Before Sorting : " + footballTeam);
Collections.sort(footballTeam);
System.out.println("After Sorting : " + footballTeam);
}
Before Sorting : [John, Roger, Steven]
After Sorting : [Steven, John, Roger]
결과로 Collections.sort를 수행하면 리스트에 저장되어 있던 선수들의 이름이 오름차순으로 정렬된 것을 확인할 수 있습니다.
Comparator 인터페이스
Comparator 인터페이스는 객체 간의 일반적인 정렬이 아닌 특정한 정렬을 수행할 때 사용할 수 있으며 Comparator 인터페이스를 상속받아 인터페이스 내 compare(arg1, arg2) 함수를 오버라이딩 하여 정렬 방법에 대해 정의하며, compare 함수는 Comparable 인터페이스에서의 compareTo 함수와 유사하게 동작합니다.
Comparable 인터페이스를 통해 선수들을 순위에 따라 정렬했던 것과 동일하게 Comparator 인터페이스를 통해 순위에 따라 선수들을 정렬할 수 있고 선수들의 나이를 기준으로 정렬하는 것도 가능합니다.
public class PlayerRankingComparator implements Comparator<Player> {
@Override
public int compare(Player firstPlayer, Player secondPlayer) {
return Integer.compare(firstPlayer.getRanking(), secondPlayer.getRanking());
}
}
public class PlayerAgeComparator implements Comparator<Player> {
@Override
public int compare(Player firstPlayer, Player secondPlayer) {
return Integer.compare(firstPlayer.getAge(), secondPlayer.getAge());
}
}
위와 같이 PlayerRankingComparator, PlayerAgeComparator를 구현하고 Collections.sort를 수행하기 위해서는 2개의 매개변수를 받아 정렬을 수행하므로 다음과 같이 코드를 작성해야 합니다.
PlayerRankingComparator playerComparator = new PlayerRankingComparator();
Collections.sort(footballTeam, playerComparator);
Before Sorting : [John, Roger, Steven]
After Sorting by ranking : [Steven, John, Roger]
PlayerAgeComparator playerComparator = new PlayerAgeComparator();
Collections.sort(footballTeam, playerComparator);
Before Sorting : [John, Roger, Steven]
After Sorting by age : [Roger, John, Steven]
Java 8에서부터는 Comparator 인터페이스에 대해 람다식 표현을 사용할 수 있으며 comparing() 정적 팩토리 함수 또한 지원하고 있습니다.
-
람다식 표현을 이용한 Comparator 사용
Comparator byRanking =
(Player player1, Player player2) -> Integer.compare(player1.getRanking(), player2.getRanking());
-
Comparator.comparing 함수 사용
Comparator<Player> byRanking = Comparator
.comparing(Player::getRanking);
Comparator<Player> byAge = Comparator
.comparing(Player::getAge);