0Pricing
Java Academy · درس

الفرز متعدد المفاتيح باستخدام thenComparing

سلسل المقارنات باستخدام thenComparing للفرز حسب عدة حقول وفق ترتيب الأولوية

الفرز متعدد المفاتيح باستخدام thenComparing درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

الفرز متعدد المفاتيح

تربط thenComparing() المقارنات لفرز العناصر وفق حقول متعددة. يُطبَّق الفرز الأساسي أولًا، ثم الفرز الثانوي عند التساوي، وهكذا.

thenComparing الأساسي

استخدم thenComparing() لكسر حالات التعادل بعد Comparator الأساسي.

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

فرز من ثلاثة مستويات

اربط العدد الذي تحتاج إليه من مستويات 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()));

thenComparing مع Comparator

استخدم التحميل الزائد الذي يقبل 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 صريح.

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

فرز مجموعات من Records

تعمل Records التي تحتوي على أساليب accessor بسلاسة مع مراجع الأساليب ضمن سلاسل المقارنات.

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()));
    });

ضمان استقرار الفرز

يستخدم List.sort() في Java و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.comparing مع Comparator للمفتاح

يتيح الشكل ذو الوسيطين 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]

تطبيق عملي: نتائج البحث عن الرحلات الجوية

فرز نتائج البحث عن الرحلات الجوية حسب السعر، ثم المدة، ثم وقت المغادرة.

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()));
});

اختبار سريع

تريد فرز الموظفين أولًا حسب القسم ترتيبًا أبجديًا، ثم حسب الراتب ترتيبًا تنازليًا. أي سلسلة صحيحة؟

خلاصة: الفرز متعدد المفاتيح باستخدام thenComparing

أهم النقاط:

  • تضيف thenComparing() مفاتيح فرز ثانوية تُطبَّق عندما تتساوى المفاتيح الأساسية
  • يمكن تسلسل أي عدد تحتاج إليه من مستويات thenComparing
  • استخدم thenComparing(Comparator) مع reversed() عندما يحتاج المفتاح الثانوي إلى ترتيب تنازلي
  • ادمج ذلك مع nullsFirst/nullsLast للحقول الاختيارية
  • تستخدم Java طريقة List.sort() المستقرة، لذلك تحتفظ العناصر المتساوية بترتيبها الأصلي
  • الاختيار الديناميكي للفرز: استخدم switch لإرجاع Comparator المناسب أثناء التشغيل

الأسئلة الشائعة

هل درس «الفرز متعدد المفاتيح باستخدام thenComparing» مجاني؟

نعم — نص درس «الفرز متعدد المفاتيح باستخدام thenComparing» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «الفرز متعدد المفاتيح باستخدام thenComparing»؟

سلسل المقارنات باستخدام thenComparing للفرز حسب عدة حقول وفق ترتيب الأولوية تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «الفرز متعدد المفاتيح باستخدام thenComparing»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. واجهة Comparable
  2. الفرز باستخدام Comparator وLambda
  3. الفرز متعدد المفاتيح باستخدام thenComparing
  4. فرز المصفوفات والمجموعات عمليًا
← العودة إلى Java Academy