Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

واجهة Comparator

جزء من قسم Object Oriented Programming في رحلة Java على Coddy — الدرس 45 من 87.

بينما يحدد Comparable الترتيب الطبيعي للفئة، قد تحتاج أحياناً إلى فرز الكائنات بطرق مختلفة. تتيح لك واجهة Comparator إنشاء منطق مقارنة منفصل دون تعديل الفئة الأصلية.

يُعد Comparator كائناً خارجياً يقارن بين كائنين من نفس النوع. هذا مفيد عندما تريد خيارات فرز متعددة أو عندما لا يمكنك تعديل الفئة (class) نفسها:

import java.util.Comparator;

class NameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
}

class AgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
}

يمكنك بعد ذلك تمرير المقارن (comparator) إلى دوال الفرز:

List<Person> people = new ArrayList<>();
people.add(new Person("Charlie", 30));
people.add(new Person("Alice", 25));

Collections.sort(people, new NameComparator());  // مرتب حسب الاسم
Collections.sort(people, new AgeComparator());   // مرتب حسب العمر

للحصول على كود مختصر، يمكنك استخدام تعبيرات لامدا (lambda expressions) بما أن Comparator هي واجهة وظيفية (functional interface):

Collections.sort(people, (p1, p2) -> p1.getName().compareTo(p2.getName()));

الفرق الجوهري: يتم تنفيذ Comparable بواسطة الفئة التي تتم مقارنتها ويحدد ترتيباً طبيعياً واحداً، بينما Comparator هو فئة منفصلة يمكنها تحديد ترتيبات مخصصة غير محدودة.

challenge icon

التحدي

سهل

لنقم ببناء نظام لفرز الموظفين يوضح قوة واجهة Comparator. ستقوم بإنشاء مقارنات متعددة لفرز الموظفين بطرق مختلفة — حسب الراتب وحسب الاسم — دون تعديل فئة Employee نفسها.

ستقوم بتنظيم الكود الخاص بك عبر أربعة ملفات:

  • Employee.java: قم بإنشاء فئة تمثل موظفاً بثلاثة حقول خاصة: name (String)، و department (String)، و salary (double). قم بتضمين منشئ (constructor) لتهيئة جميع الحقول وطرق جلب (getter methods) لكل منها. قم بإعادة كتابة toString() لتعيد: [name] ([department]) - $[salary]
  • SalaryComparator.java: قم بإنشاء مقارن يقوم بفرز الموظفين حسب الراتب بترتيب تنازلي (الأعلى أجراً أولاً). قم بتنفيذ واجهة Comparator<Employee> وطريقة compare() الخاصة بها. استخدم Double.compare() للمقارنة الآمنة لقيم الرواتب.
  • NameComparator.java: قم بإنشاء مقارن آخر يقوم بفرز الموظفين أبجدياً حسب الاسم بترتيب تصاعدي (من A إلى Z). قم بتنفيذ Comparator<Employee> واستخدم طريقة compareTo() الخاصة بـ String للمقارنة.
  • Main.java: اجمع كل شيء معاً عن طريق إنشاء قائمة من الموظفين وفرزهم باستخدام المقارنات الخاصة بك. ستتلقى مدخلات لثلاثة موظفين، كل منهم في سطر منفصل بالتنسيق: name,department,salary

    قم بإنشاء ArrayList من الموظفين، ثم استعرض كلا نهجي الفرز:

    1. أولاً، قم بالفرز حسب الراتب باستخدام SalaryComparator الخاص بك واطبع كل موظف
    2. اطبع سطراً فارغاً
    3. ثم قم بالفرز حسب الاسم باستخدام NameComparator الخاص بك واطبع كل موظف

ستتلقى ثلاثة أسطر من المدخلات، يحتوي كل منها على بيانات الموظف بالتنسيق: name,department,salary

على سبيل المثال، قد يبدو سطر الإدخال كما يلي: Alice,Engineering,75000.0

تذكر استيراد java.util.Comparator في ملفات المقارن الخاصة بك، و java.util.ArrayList و java.util.Collections و java.util.Scanner في ملف Main الخاص بك. لاحظ كيف تسمح لك المقارنات بتعريف استراتيجيات فرز متعددة خارجياً — لا تحتاج فئة Employee إلى معرفة أي شيء عن كيفية فرزها!

ورقة مرجعية

تتيح لك واجهة Comparator إنشاء منطق مقارنة خارجي لفرز الكائنات دون تعديل الفئة الأصلية. هذا مفيد عندما تحتاج إلى خيارات فرز متعددة.

تقوم واجهة Comparator بمقارنة كائنين من نفس النوع من خلال تنفيذ طريقة compare():

import java.util.Comparator;

class NameComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
}

قم بتمرير الـ comparator إلى طرق الفرز:

Collections.sort(people, new NameComparator());

يمكنك استخدام تعبيرات لامبدا (lambda expressions) للحصول على كود موجز لأن Comparator هي واجهة وظيفية (functional interface):

Collections.sort(people, (p1, p2) -> p1.getName().compareTo(p2.getName()));

لمقارنة القيم الرقمية، استخدم طرق فئات الغلاف (wrapper class) مثل Double.compare() لإجراء مقارنة آمنة:

class AgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() - p2.getAge();
    }
}

الفرق الرئيسي: تحدد Comparable ترتيباً طبيعياً واحداً داخل الفئة نفسها، بينما Comparator هي فئة منفصلة يمكنها تحديد ترتيبات مخصصة غير محدودة خارجياً.

جرّب بنفسك

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // قراءة ثلاثة موظفين من المدخلات
        // تنسيق كل سطر: الاسم، القسم، الراتب
        String line1 = scanner.nextLine();
        String line2 = scanner.nextLine();
        String line3 = scanner.nextLine();
        
        // TODO: قم بتحليل كل سطر وإنشاء كائنات موظف (Employee)
        // تلميح: استخدم split(",") لفصل القيم
        
        // TODO: قم بإنشاء ArrayList للموظفين وأضف الثلاثة جميعاً
        
        // TODO: قم بالفرز حسب الراتب باستخدام SalaryComparator واطبع كل موظف
        // تلميح: استخدم Collections.sort(list, comparator)
        
        // TODO: اطبع سطراً فارغاً
        
        // TODO: قم بالفرز حسب الاسم باستخدام NameComparator واطبع كل موظف
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming