0Pricing
Java Academy · 강의

thenComparing을 사용한 다중 키 정렬

thenComparing으로 비교자를 연결해 우선순위에 따라 여러 필드를 정렬합니다.

thenComparing을 사용한 다중 키 정렬은(는) CoddyKit의 무료 Java Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Java Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

다중 키 정렬

thenComparing()은 여러 필드로 정렬할 수 있도록 Comparator를 연결합니다. 먼저 첫 번째 기준으로 정렬하고, 값이 같으면 두 번째 기준을 적용하는 식으로 계속 진행합니다.

기본 thenComparing

첫 번째 Comparator의 결과가 같을 때 순위를 결정하려면 thenComparing()을 사용하십시오.

import java.util.*;

record Student(String name, int grade, double gpa) {}

List<Student> students = List.of(
    new Student("Alice",   10, 3.9),
    new Student("Bob",     11, 3.7),
    new Student("Charlie", 10, 3.8),
    new Student("Diana",   11, 3.7)
);

Comparator<Student> sort = Comparator.comparingInt(Student::grade)
    .thenComparingDouble(Student::gpa).reversed()
    .thenComparing(Student::name);

students.stream().sorted(sort).forEach(System.out::println);

3단계 정렬

필요한 만큼 thenComparing 단계를 연결할 수 있습니다.

record Employee(String dept, String team, String name, double salary) {}

List<Employee> employees = List.of(
    new Employee("Eng", "Backend", "Alice",   95000),
    new Employee("Eng", "Backend", "Bob",     92000),
    new Employee("Eng", "Frontend","Charlie", 88000),
    new Employee("Mkt", "Growth",  "Diana",   75000)
);

Comparator<Employee> sort = Comparator
    .comparing(Employee::dept)
    .thenComparing(Employee::team)
    .thenComparing(Comparator.comparingDouble(Employee::salary).reversed());

employees.stream().sorted(sort)
    .forEach(e -> System.out.printf("%s > %s > %s: $%.0f%n",
        e.dept(), e.team(), e.name(), e.salary()));

Comparator와 thenComparing

두 번째 키에 역순 또는 사용자 지정 순서가 필요하면 Comparator를 받는 오버로드를 사용하십시오.

Comparator<Employee> sort = Comparator
    .comparing(Employee::dept)
    .thenComparing(Comparator.comparing(Employee::salary).reversed()); // highest paid first
    // equivalent: .thenComparingDouble(Employee::salary, Comparator.reverseOrder())

null 필드를 포함한 정렬

선택적 필드를 자연스럽게 처리하려면 thenComparing을 nullsLast/nullsFirst와 함께 사용하십시오.

record Product(String name, String category, Double rating) {}

Comparator<Product> sort = Comparator
    .comparing(Product::category, Comparator.nullsLast(Comparator.naturalOrder()))
    .thenComparing(Product::rating,  Comparator.nullsLast(Comparator.reverseOrder()))
    .thenComparing(Product::name);

동적 정렬 키 선택

사용자 설정에 따라 실행 중에 정렬 키를 선택합니다.

static Comparator<Product> sortBy(String key) {
    return switch (key) {
        case "price"    -> Comparator.comparingDouble(Product::price);
        case "name"     -> Comparator.comparing(Product::name);
        case "stock"    -> Comparator.comparingInt(Product::stock).reversed();
        default         -> Comparator.comparingDouble(Product::price);
    };
}

String userChoice = "stock";
products.stream().sorted(sortBy(userChoice)).forEach(System.out::println);

추출기 함수와 thenComparing

thenComparing(Function<T,U>) 오버로드는 명시적인 Comparator 없이 두 번째 정렬 기준에 사용할 Comparable 키를 추출합니다.

record Event(String type, int year, int month, int day) {}

Comparator<Event> chronological = Comparator
    .comparing(Event::type)
    .thenComparingInt(Event::year)
    .thenComparingInt(Event::month)
    .thenComparingInt(Event::day);

레코드 컬렉션 정렬

접근자 메서드가 있는 레코드는 Comparator 연결에서 메서드 참조와 원활하게 작동합니다.

record Order(String customerId, String status, double total, java.time.LocalDate date) {}

List<Order> orders = loadOrders();

Comparator<Order> byCustomerThenDate = Comparator
    .comparing(Order::customerId)
    .thenComparing(Order::date, Comparator.reverseOrder()); // newest first per customer

orders.stream().sorted(byCustomerThenDate)
    .collect(Collectors.groupingBy(Order::customerId))
    .forEach((cust, ords) -> {
        System.out.println("Customer: " + cust);
        ords.forEach(o -> System.out.println("  " + o.date() + " $" + o.total()));
    });

안정적인 정렬 보장

Java의 객체용 List.sort()와 Arrays.sort()는 안정적인 병합 정렬을 사용합니다. 비교 결과가 같은 요소는 원래의 상대적 순서를 유지합니다.

// Stable sort example:
// First sort by name, then by dept
// Equal dept elements preserve the name order from previous sort
List<Employee> sorted = employees.stream()
    .sorted(Comparator.comparing(Employee::name))     // 1st pass
    .sorted(Comparator.comparing(Employee::dept))     // 2nd pass (stable!)
    .collect(Collectors.toList());
// Within each dept, employees remain alphabetically sorted

키 Comparator를 사용한 Comparator.comparing

두 인수를 받는 형식인 Comparator.comparing(keyExtractor, keyComparator)을 사용하면 키와 해당 키의 정렬 순서를 모두 지정할 수 있습니다.

// Sort by name length, then alphabetically within same length
Comparator<String> c = Comparator
    .comparing(String::length, Integer::compare)
    .thenComparing(Comparator.naturalOrder());

List<String> words = new ArrayList<>(List.of("fig","apple","kiwi","banana","pea"));
words.sort(c);
System.out.println(words); // [fig, pea, kiwi, apple, banana]

실전: Flight 검색 결과

가격, 소요 시간, 출발 시간 순으로 Flight 검색 결과를 정렬합니다.

record Flight(String airline, double price, int durationMin, java.time.LocalTime departs) {}

Comparator<Flight> bestFlight = Comparator
    .comparingDouble(Flight::price)
    .thenComparingInt(Flight::durationMin)
    .thenComparing(Flight::departs);

정렬된 결과 수집

다중 키 정렬을 수집기와 결합하여 정렬된 그룹이나 맵을 생성합니다.

Map<String, List<Employee>> byDept = employees.stream()
    .sorted(Comparator.comparing(Employee::dept)
        .thenComparing(Comparator.comparingDouble(Employee::salary).reversed()))
    .collect(Collectors.groupingBy(Employee::dept,
        LinkedHashMap::new,  // preserve insertion (sorted) order
        Collectors.toList()));

byDept.forEach((dept, emps) -> {
    System.out.println(dept + ":");
    emps.forEach(e -> System.out.println("  " + e.name() + " $" + e.salary()));
});

빠른 확인

직원을 먼저 부서 이름의 알파벳순으로 정렬한 다음 salary의 내림차순으로 정렬하려고 합니다. 올바른 연결은 무엇입니까?

복습: thenComparing을 사용한 다중 키 정렬

핵심 내용:

  • thenComparing()은 기본 키가 같을 때 적용되는 보조 정렬 키를 추가합니다
  • 필요한 만큼 thenComparing 단계를 연결할 수 있습니다
  • 보조 키를 내림차순으로 정렬해야 할 때는 reversed()와 함께 thenComparing(비교자)을 사용합니다
  • 선택적 필드에는 nullsFirst/nullsLast를 함께 사용합니다
  • Java의 List.sort()는 안정적이므로 값이 같은 요소는 원래 순서를 유지합니다
  • 동적 정렬 선택: 실행 시 올바른 비교자를 반환하도록 switch를 사용합니다

자주 묻는 질문

“thenComparing을 사용한 다중 키 정렬” 강의는 무료인가요?

네 — “thenComparing을 사용한 다중 키 정렬” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Java Academy 강의 전체를 잠금 해제할 수 있습니다. Java Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“thenComparing을 사용한 다중 키 정렬”에서 뭘 배우나요?

thenComparing으로 비교자를 연결해 우선순위에 따라 여러 필드를 정렬합니다. 브라우저에서 직접 실행하는 실습 코드로 Java Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Java Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Java Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“thenComparing을 사용한 다중 키 정렬” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Java Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Java Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Comparable 인터페이스
  2. Comparator와 람다 정렬
  3. thenComparing을 사용한 다중 키 정렬
  4. 배열과 컬렉션 정렬 실습
← Java Academy(으)로 돌아가기