객체 정렬하기

|

정렬 대상 클래스

public class Player {
    private String name;
    private int score;

    public Player(String name, int score) {
        this.name = name;
        this.score = score;
    }

    // Getters, Setters 생략
}

List<Player> players = new ArrayList<>();
players.add(new Player("Alice", 899));
players.add(new Player("Bob", 982));
players.add(new Player("Chloe", 1090));
players.add(new Player("Dale", 982));
players.add(new Player("Eric", 1018));

객체 정렬 기준의 필요성

단순한 숫자나 문자와 같은 기본형(primitive) 데이터는 사람들이 하는 일반적으로 받아드리는 대소 비교라는 개념이 있습니다. (Natural Order)
따라서 우리는 1보다는 2가 크며, 'A' 보단 'B'가 크다는 것을 알기 때문에 자바는 이런 통념에 따라 정렬을 해줍니다.

예를 들어, 게이머들의 점수만으로 별도의 배열을 만들면 다음과 같이 Arrays.sort() 메서드를 사용하여 정렬을 할 수 있습니다.

int[] scores = {899, 982, 1090, 982, 1018};
Arrays.sort(scores);
System.out.println(Arrays.toString(scores)); // [899, 982, 982, 1018, 1090]

Comparable 인터페이스

객체의 정렬 기준을 정의하는 첫번째 방법은 정렬 대상 클래스를 자바에서 기본적으로 제공하고 있는 Comparable 인터페이스를 구현하도록 변경하는 것입니다.
이를 적용하면 Player 클래스는 다음과 같이 수정됩니다.

public class Player implements Comparable<Player> {
    // Fields, Getters, Setters 생략

    @Override
    public int compareTo(Player o) {
        return o.getScore() - getScore();
    }
}

Comparable 인터페이스의 compareTo() 메서드를 통해 인자로 넘어온 같은 타입의 다른 객체와 대소 비교가 가능합니다. 메서드를 호출하는 객체가 인자로 넘어온 객체보다 작을 경우에는 음수를 리턴하고, 크기가 동일하다면 0, 클 경우에는 양수를 리턴해야합니다.

게이머 랭키 페이지의 경우 높은 점수 순으로 내림 차순 정렬을 원하기 때문에, 인자로 넘어온 게이머의 점수에서 메서드를 호출하는 게이머의 점수를 빼면 됩니다. 예를 들어 인자로 넘어온 게이머의 점수가 982이고 compareTo() 메서드를 호출하는 객체의 점수가 1018이라면, compareTo() 메서드는 음수(982-1018)를 리턴하게 되며, 이는 곧 메서드를 호출하는 게미머가 인자로 넘어온 게이머보다 작다는 것을 의미합니다. 다시 말해, 메서드를 호출를 호출하는 메서드가 점수가 더 크지만, 객체 자체 비교에서는 인자로 넘어온 게이머보다 작은 객체가 됩니다.

 

Comparator 객체 사용

만약 정렬 대상 클래스의 코드를 직접 수정할 수 없는 경우에는 어떻게 객체의 정렬 기준을 정의할 수 있을까요? 또는 정렬 하고자 하는 객체에 이미 존재하고 있는 정렬 기준과 다른 정렬 기준으로 정렬을 하고 싶을 때는 어떻게 해야할까요?

이 때 필요한 것이 바로 Comparable 인터페이스와 이름이 유사한 Comparator 인터페이스입니다. Comparator 인터페이스의 구현체를 Arrays.sort()나 Collections.sort()와 같은 정렬 메서드의 추가 인자로 넘기면 정렬 기준을 누락된 클래스의 객체나 기존 정렬 기준을 무시하고 새로운 정렬 기준으로 객체를 정렬할 수 있습니다.

Comparator<Player> comparator = new Comparator<Player>() {
    @Override
    public int compare(Player a, Player b) {
        return b.getScore() - a.getScore();
    }
};

Collections.sort(players, comparator);
System.out.println(players); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

위 코드는 Comparator 객체를 Collections.sort() 메서드의 두번째 인자로 넘겨서 이전 섹션과 동일한 정렬 결과를 만들어 내고 있습니다. 이렇게 Comparator 객체를를 인자로 넘기면, 정렬 대상 객체가 Comparable 인터페이스를 구현 여부와 상관없이, 넘어온 Comparator 구현체의 compare() 메서드 기준으로 정렬을 수행합니다.
compare() 메서드는 비교 대상 2 개의 객체를 인자를 차례로 인자로 받습니다. 첫번째 인자가 두번째 인자보다 작다면 음수, 같다면 0, 크다면 양수를 리턴하면 됩니다.

 

Stream 으로 정렬

Stream 클래스의 sorted() 메서드도 Comparator 객체를 인자로 받아 정렬을 해줍니다.
스트림을 사용하면 위에서 살펴본 배열과 리스트의 정렬과 달리 기존 객체의 순서를 변경하지 않고, 새롭게 정렬된 객체를 생성하고자 할 때 사용됩니다.

List<Player> sortedPlayers = players.stream()
        .sorted((a, b) -> b.getScore() - a.getScore())
        .collect(Collectors.toList());
System.out.println(sortedPlayers); // [Player(name=Chloe, score=1090), Player(name=Eric, score=1018), Player(name=Bob, score=982), Player(name=Dale, score=982), Player(name=Alice, score=899)]

'Java' 카테고리의 다른 글

주차관리 시스템 ver.2  (0) 2019.07.24
Java - Stream, Network  (0) 2019.07.23
Java - Stream(byte, 문자)  (0) 2019.07.22
Java - Thread(싱글, 다중(멀티), 스위칭), 동기화  (0) 2019.07.19
Java - 제네릭스, Wildcard, 열거형, Thread  (0) 2019.07.18
And