ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • compareTo() 메소드와 오름차순, 내림차순이 무슨 상관?
    Java 2022. 8. 14. 20:27

    무지성으로 코딩하다

    객체가 가진 속성을 기준으로 내림차순을 하려고 한다. Comparable<T> 인터페이스의 compareTo(T o) 메소드를 구현하려다가 "아 현재 인스턴스에서 비교 인스턴스 속성을 빼는 거였나? ...그 반댄가?"

     

    그간 구현에만 집중했다. 일단 해보고 결과가 반대로 나오면 반대로 연산해서 코드를 구현해놓고, 이따가 알아보고 자야지 하고 잊고 넘어갔다.

     

    지금 구현하고 있는 코드의 예시를 통해 이해하고 넘어가자.


    예시 코드와 출력문

    • 자동차에 랜덤한 숫자가 부여되고 랜덤 값이 4이상일 경우 전진한다.

     

    이를 구현하기 위해 정의해 놓은 Car 코드는 다음과 같다. (compareTo 메소드를 살펴보기 위해서 중간 코드들은 생략함)

    public class Car implements Comparable<Car> {
    
        private final CarName name;
        private final CarPosition position;		<- 여기서 쓰일 데이터는 position
    
    	...
        
        @Override
        public int compareTo(Car o) {	
            return o.position.getNumber() - this.position.getNumber();	<- 내림차순을 구현하였다.
        }
    
    }
    @Test
        void compareTo_test() {
        	// 3개의 자동차에 각각 이름을 부여하고 생성했다.
            Car h = new Car(new CarName("h"));
            Car prism = new Car(new CarName("prism"));
            Car dev = new Car(new CarName("dev"));
            
            // 자동차들의 default position은 1로 설정되어있다.
            h.move(4);	<- move()메소드의 매개변수 값이 4 이상이면 position은 +1이 된다.
            h.move(5);
            h.move(6);
            prism.move(2);
            prism.move(3);
            prism.move(1);
            dev.move(4);
            dev.move(2);
            dev.move(2);
    
            List<Car> cars = new ArrayList<>();
            cars.add(h);
            cars.add(prism);
            cars.add(dev);
    
            for (Car car : cars) {
            	// cars를 정렬하기 전 출력문
                System.out.println("[before] " + car.getName() + " : " + car.getPosition().getNumber());
            }
    
            System.out.println("───────────────────");
            Collections.sort(cars);		<- cars를 정렬 !
            for (Car car : cars) {
            	// cars 정렬 후 출력문
                System.out.println("[after] " + car.getName() + " : " + car.getPosition().getNumber());
            }
        }

    출력문이 어떻게 찍힐까? 결과를 예상해보자. 모든 차들의 default position은 1로 셋팅되어있다.

    • h 자동차는 랜덤값이 차례로 4,5,6 이 나왔기 때문에 3칸 전진한다. (total 4)
    • prism 자동차는 랜덤값이 차례로 2,3,1 이 나왔기 때문에 한 칸도 전진할 수 없다. (total 1)
    • dev 자동차는 랜덤값이 차례로 4,2,2 이 나왔기 때문에 1칸 전진한다. (total 2)
    [before] h : 4
    [before] prism : 1
    [before] dev : 2
    ───────────────────
    [after] h : 4
    [after] dev : 2
    [after] prism : 1

    compareTo() 메소드를 구현하는 게 내림차순이랑 무슨 상관인가?

    Collections.sort(cars) 메소드를 기준으로 before & after 출력문의 결과가 다르다.

    cars를 정렬하고 났더니 h -> dev -> prism 순으로 정렬이 되었다. Collections 클래스의 sort() 메소드를 살펴보자.

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }
    • sort 메소드는 List<T> list 를 매개변수로 받고 있는데, list의 타입인 List<T>는 Comparable<? super T> 를 상속하고 있는 타입이어야 한다.
    • 위의 Car 클래스를 다시 보자. Comaparable 인터페이스를 구현하고 있다. 내부에 compareTo(Car o) 메소드를 구현하고 있다.
    • 즉, Collections.sort(cars) 코드는 compareTo(Car o) 메소드를 실행하여 정렬을 하였다.

     

    ⭐ 이제 compareTo() 메소드와 내림차순이 무슨 관련이 있는지 살펴보자. 

    public interface Comparable<T> {
        /**
         * Compares this object with the specified object for order.  Returns a
         * negative integer, zero, or a positive integer as this object is less
         * than, equal to, or greater than the specified object.
         * ......
         * @param   o the object to be compared.
         * @return  a negative integer, zero, or a positive integer as this object
         *          is less than, equal to, or greater than the specified object.
         */
        public int compareTo(T o);
    }
    • 리턴되는 값을 살펴보자.
    • 리턴되는 값이 음수이면 현재 인스턴스가 비교대상인 인스턴스보다 작다.
    • 리턴되는 값이 0이면 현재 인스턴스와 비교대상인 인스턴스가 같다.
    • 리턴되는 값이 양수이면 현재 인스턴스가 비교대상인 인스턴스보다 크다.

     

    정렬되기 전 List<Car> 타입의 cars는 add한 순서대로 [h] -> [prism] -> [dev] 저장되어있다.

      name position
    Car h 4
    Car prism 1
    Car dev 2

     

    @Override
    public int compareTo(Car o) {
        return o.position.getNumber() - this.position.getNumber();	<- 내림차순
    }
    • h와 prism을 비교한다.
    • prism.position.getNumber() 은 1이고, this.position.getNumber()는 4 이므로 리턴값은 -3 (음수)
    • 리턴되는 값이 음수이면 현재 인스턴스(h)가 비교대상인 인스턴스(prism)보다 작다.
    • [h] -> [prism] 순으로 정렬한다.
    • prism과 dev를 비교한다.
    • dev.position.getNumber() 은 2이고, prism.position.getNumber()는 1 이므로 리턴값은 1 (양수)
    • [dev] -> [prism]
    • 때문에 정렬한 결과는 [h] -> [dev] -> [prism] 순이 된다. (내림차순)
      name position
    Car h 4
    Car dev 2
    Car prism 1

     

     

    그럼 오름차순을 하고 싶으면?

    @Override
    public int compareTo(Car o) {
        return this.position.getNumber() - o.position.getNumber();	<- 오름차순
    }
    • 연산을 위 코드처럼 반대로 하면 오름차순이 되고 결과는 아래와 같다.
    [before] h : 4
    [before] prism : 1
    [before] dev : 2
    ───────────────────
    [after] prism : 1
    [after] dev : 2
    [after] h : 4

    댓글