Java

[JAVA] Comparable, Comparator

개바리여리 2025. 12. 13. 19:08

Comparable 인터페이스는 compareTo 메서드를 재정의하여 두 객체를 비교할때 사용된다.

"객체 스스로가 정렬 기준을 정의하도록 만드는 인터페이스"

 

String, Date, Integer, Character, Double 등의 wrapper 클래스는 이미 Comparable 인터페이스가 구현되어 있다.

public interface Comparable<T> {
	int compareTo(T o);
}

 

자기 자신(this) 과 다른 객체 (o)를 비교한다. 

this < o :  음수 리턴

this == 0 : 0 리턴

this > 0 : 양수리턴

* Comparable 구현이 안되어 있으면 정렬이 불가능하다.

 

public class Person implements Comparable<Person> {

    private String name;
    private int age;
    
    @Override
    public int compareTo(Person o) {
        // 나이 오름차순
        return Integer.compare(this.age, o.age);
        
        //내림차순이라면 아래와 같이
        //return Interger.compare(o.age, this.age);
    }
    
    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}

public class ComparableTest {

    public static void main(String[] args) {

        List<Person> list = new ArrayList<>();
        list.add(new Person("철수", 30));
        list.add(new Person("영희", 25));
        list.add(new Person("민수", 40));
        list.add(new Person("지훈", 25));

        System.out.println("정렬 전:");
        System.out.println(list);

        Collections.sort(list);

        System.out.println("정렬 후 (나이 오름차순):");
        System.out.println(list);
    }
}

 

Collections.sort(list)를 호출하면
정렬 과정에서 필요한 순간마다 Person.compareTo(other)를 반복 호출해서
그 리턴값(음수/0/양수)을 기준으로 순서를 바꾼다. 값의 크기보다는 양수냐 음수냐가 중요함.

 

 

Comparator

 

정렬 기준을 객체 바깥에서 주입하는 인터페이스

public interface Comparator<T> {
	int compare(T o1, T o2);
}

 

 

  • o1 과 o2 두 객체를 외부에서 비교
  • 객체 자신은 아무것도 모름
  • “이번엔 이렇게 정렬해줘” 하고 그때그때 기준을 바꿈
  Comparable Comparator
위치 클래스 내부 클래스 외부
메서드 compareTo(this vs o) compare(o1 vs o2)
정렬 기준 고정 (자연 정렬) 유동적 (상황별)
개수 보통 1개 여러 개
수정 영향 클래스 수정 필요 클래스 수정 없음
실무 용도 도메인 기본 정렬 화면 / 조건 / 버튼

 

 

 

Collections.sort(list, new Comparator<Person>(){
	@Override
    public int compare(Person a, Person b){
    	return a.getName().compareTo(b.getName());
    }
});

 

 

실습 예제

 

public class Order implements Comparable<Order>{

    private Long id;
    private String customerName;
    private int totalPrice;
    private OrderStatus status;
    private LocalDateTime orderDate;

    public Order(Long id, String customerName, int totalPrice,
                 OrderStatus status, LocalDateTime orderDate) {
        this.id = id;
        this.customerName = customerName;
        this.totalPrice = totalPrice;
        this.status = status;
        this.orderDate = orderDate;
    }

    //기본정렬 : 주문일 최신순
    @Override
    public int compareTo(Order o) {
        return o.orderDate.compareTo(this.orderDate);
    }

    public String getCustomerName() { return customerName; }
    public int getTotalPrice() { return totalPrice; }
    public OrderStatus getStatus() { return status; }
    public LocalDateTime getOrderDate() { return orderDate; }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", customer='" + customerName + '\'' +
                ", price=" + totalPrice +
                ", status=" + status +
                ", date=" + orderDate +
                '}';
    }
}

 

public enum OrderStatus {
    READY("주문접수"), //주문접수
    PAID("결제완료"), //결제완료
    SHIPPED("출고"), //출고
    DONE("완료"); //완료

    private final String description;

    OrderStatus(String description){
        this.description = description;
    }

    public String getDescription(){
        return this.description;
    }

}


public class OrderSortTest {

    public static void main(String[] args){

        List<Order> orders = new ArrayList<>();

        orders.add(new Order(1L, "김철수", 30000, OrderStatus.PAID,
                LocalDateTime.now().minusHours(2)));
        orders.add(new Order(2L, "이영희", 120000, OrderStatus.READY,
                LocalDateTime.now().minusDays(1)));
        orders.add(new Order(3L, "박민수", 50000, OrderStatus.SHIPPED,
                LocalDateTime.now().minusHours(5)));
        orders.add(new Order(4L, "김철수", 70000, OrderStatus.DONE,
                LocalDateTime.now().minusDays(2)));

        System.out.println("=== 기본 정렬 (주문일 최신순) ===");
        Collections.sort(orders);
        orders.forEach(System.out::println);

        //금액 높은순 정렬 (Comparator)
        System.out.println("\n=== 금액 높은 순 ===");
        orders.sort(
                Comparator.comparing(Order::getTotalPrice).reversed()
        );
        orders.forEach(System.out::println);



        System.out.println("\n=== 상태별 정렬 ===");
        Map<OrderStatus, Integer> statusOrder = Map.of(
                OrderStatus.READY, 1,
                OrderStatus.PAID, 2,
                OrderStatus.SHIPPED, 3,
                OrderStatus.DONE, 4
        );
        orders.sort(
                Comparator.comparing(o -> statusOrder.get(o.getStatus()))
        );

        // status 정렬하는데 같으면 orderDate 기준으로 내림차순 정렬
        System.out.println("\n=== 상태 → 주문일 최신순 ===");
        orders.sort(
                Comparator.comparing(Order::getStatus)
                        .thenComparing(Order::getOrderDate, Comparator.reverseOrder())
        );

    }
}