반응형

 

 

 

TreeSet 에서  비교 클래슬를 생성자에서 받을때는 Comparable 을 상속받은 클래스는 불가하고

Comparator 를 상속받은 클래스를 생성자에 Functor 식으로 넣어서 비교기준으로 삼는 것은 가능하다

class TestCopmp implements Comparator {

    public int a;

    @Override
    public int compare(Object o1, Object o2) {
        return ((Test)o1).a - ((Test)o2).a;
    }
}

 

Set set = new TreeSet( new TestCopmp() );

마치 C++ 에서 Fuctor 를 만든것 처럼 끝 어미도 tor 이다, 사용법도 유사하다 볼수 있다

Comparator 는 sort 시에도 정렬 기준으로 던져줄수 있는 Fuctor 다

 

 

 

위 처럼 생성자에 넣는 것이 아닌 클래스 자체에 비교 함수를 override 하는 방법이 있는데 그것이 Comparable 이다

class Test implements Comparable {
    public int a;

    @Override
    public int compareTo(Object o) {
        return a - ((Test)o).a;
    }
}
//원소 추가함과 동시에 정렬됨
Set set = new TreeSet();

for (int i=0; set.size() < 6;++i)
{
    int nm = (int)(Math.random()* 45) + 1;
    Test ss= new Test();
    ss.a = nm;

    set.add(ss);

}

 

 위 처럼 하면 가능 하지만 Test 클래스가 Comparator 를 상속을 받는것은 가능하나
 객체간의 비교로 사용용도로 사용되진 못한다, 사용하면 크래쉬 (Comparable 로 캐스팅 하지 못한다는 에러 발생)

Exception in thread "main" java.lang.ClassCastException: class Test cannot be cast to 
class java.lang.Comparable (Test is in unnamed module of loader 'app'; 
java.lang.Comparable is in module java.base of loader 'bootstrap')

	at java.base/java.util.TreeMap.compare(TreeMap.java:1563)
	at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:768)
	at java.base/java.util.TreeMap.put(TreeMap.java:777)
	at java.base/java.util.TreeMap.put(TreeMap.java:534)
	at java.base/java.util.TreeSet.add(TreeSet.java:255)
	at comp.main(comp.java:98)

 

 

 

 

 

 

[Comparable 의 사용 예시]

import java.util.*;

//하나의 자바 소스 파일에 작성된 클래스 중 오직 한 클래스만 public으로 선언할 수 있습니다.

//Comparable 상속받아 정렬 기준을 만들수 있다
class Player implements Comparable<Player>
{
    private String name;
    private int score;

    public int getScore(){ return score; }

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

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

public class comp {

    public static void main(String[] args)
    {
        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));

        //이때 Comparable 이 사용됨
       주석 제거하면 Comparable 가 작동하여 정렬된다
        //Collections.sort(players);


        //comparator 를 통해 클래스 밖에서 정렬 기준을 다르게 하여 정렬 할수 있다
        Comparator<Player> comparator = new Comparator<Player>() {
            @Override
            public int compare(Player a, Player b)
            {
                return a.getScore() - b.getScore();
            }
        };

        //Collection<Integer> sdf = new LinkedList<>();
        Collections.sort(players, comparator);


    }

}

 

 

 

 

 

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)를 리턴하게 되며, 이는 곧 메서드를 호출하는 게미머가 인자로 넘어온 게이머보다 작다는 것을 의미합니다. 다시 말해, 메서드를 호출를 호출하는 메서드가 점수가 더 크지만, 객체 자체 비교에서는 인자로 넘어온 게이머보다 작은 객체가 됩니다.

 

Collections.sort(players);
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)]

 

 

이제 Collections.sort() 메서드에는 Comparable 인터페이스를 구현한 Comparable 타입의 Player 객체의 리스트가 인자로 넘어가기 때문에 더 이상 컴파일 에러가 발생하지 않습니다. 정렬 후 게이머 리스트를 출력해보면 원하는 바와 같이 점수가 제일 높은 Chloe 가 리스트의 맨 앞으로 위치하고, 점수가 제일 낮은 Alice 가 리스트의 맨 뒤에 위치하게 됩니다.

 

 

 

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, 크다면 양수를 리턴하면 됩니다.

 

 

람다 함수로 대체

Comparator 객체는 메서드가 하나 뿐인 함수형 인터페이스를 구현하기 때문에 람다 함수로 대체가 가능합니다.

 

 

Collections.sort(players, (a, b) -> b.getScore() - a.getScore());
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)]

 

 

 

 

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)]

 

 

 

ref : https://www.javastudypoint.com/2019/03/comparable-vs-comparator-in-java.html

ref : https://www.daleseo.com/java-comparable-comparator/

ref :https://server-engineer.tistory.com/808

반응형

+ Recent posts