Comparable وComparator في جافا مفيدة جدًا لفرز مجموعة من الكائنات. توفر جافا بعض الأساليب المضمنة لفرز مصفوفة الأنواع الأولية أو مصفوفة الكائنات المحيطة أو القائمة. هنا سنتعلم أولاً كيفية فرز مصفوفة / قائمة من الأنواع الأولية وكلاسات المحيطة ، ثم سنستخدم واجهتي java.lang.Comparable و java.util.Comparator لفرز مصفوفة / قائمة من الكلاسات المخصصة. دعنا نرى كيف يمكننا فرز أنواع الأولية أو مصفوفة الكائنات والقائمة باستخدام برنامج بسيط.
package com.journaldev.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaObjectSorting {
/**
* This class shows how to sort primitive arrays,
* Wrapper classes Object Arrays
* @param args
*/
public static void main(String[] args) {
//فرز مصفوفة الأنواع الأولية مثل مصفوفة int
int[] intArr = {5,9,1,10};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr));
//فرز مصفوفة السلاسل
String[] strArr = {"A", "C", "B", "Z", "E"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr));
//فرز قائمة الكائنات من الكلاسات المحيطة
List strList = new ArrayList();
strList.add("A");
strList.add("C");
strList.add("B");
strList.add("Z");
strList.add("E");
Collections.sort(strList);
for(String str: strList) System.out.print(" "+str);
}
}
ناتج البرنامج أعلاه هو:
[1, 5, 9, 10]
[A, B, C, E, Z]
A B C E Z
الآن لنحاول فرز مصفوفة من الكائنات.
package com.journaldev.sort;
public class Employee {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
//تمت كتابة هذا لطباعة المعلومات الصديقة للمستخدم حول الموظف
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
هنا هو الكود الذي استخدمته لفرز مصفوفة من كائنات الموظفين.
//فرز مصفوفة الكائنات
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//فرز مصفوفة الموظفين باستخدام تنفيذ واجهة Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
عندما حاولت تشغيل هذا ، يلقي الاستثناء التشغيلي التالي.
Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)
Comparable وComparator
توفر جافا واجهة Comparable التي يجب تنفيذها من قبل أي فئة مخصصة إذا كنا نريد استخدام طرق ترتيب Arrays أو Collections. تحتوي واجهة Comparable على طريقة compareTo(T obj) التي يستخدمها طرق الترتيب، يمكنك التحقق من أي فئة Wrapper أو String أو Date لتأكيد ذلك. يجب علينا استبدال هذه الطريقة بطريقة تعيد عددًا سالبًا أو صفرًا أو عددًا موجبًا إذا كان “هذا” الكائن أقل من أو يساوي أو أكبر من الكائن الذي يتم تمريره كوسيط. بعد تنفيذ واجهة Comparable في فئة Employee، إليك ناتج فئة Employee.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//لنقم بفرز الموظفين استنادًا إلى معرف بترتيب تصاعدي
//تعيد عددًا سالبًا أو صفرًا أو عددًا موجبًا كمعرف الموظف هذا
//أقل من أو يساوي أو أكبر من الكائن المحدد.
return (this.id - emp.id);
}
@Override
//هذا مطلوب لطباعة المعلومات الودية للموظف
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
الآن عند تنفيذ مقطع الكود أعلاه لفرز Arrays للموظفين وطباعته، إليك الناتج.
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
كما يمكنك رؤية أن مصفوفة الموظفين مرتبة حسب الهوية بترتيب تصاعدي. ولكن، في معظم الحالات الحقيقية، نريد الترتيب بناءً على معايير مختلفة. على سبيل المثال، كرئيس تنفيذي، أود ترتيب الموظفين بناءً على الراتب، وكموظف موارد بشرية، يفضل ترتيبهم بناءً على العمر. هذا هو الوضع الذي نحتاج فيه إلى استخدام واجهة Java Comparator لأن تنفيذ طريقة Comparable.compareTo(Object o) يمكن أن يوفر الترتيب الافتراضي ولا يمكننا تغييره بشكل ديناميكي. بينما مع واجهة Comparator، يمكننا تحديد طرق متعددة للترتيب ثم اختيار طريقة الترتيب بناءً على متطلباتنا.
Java Comparator
يجب تنفيذ واجهة Comparator بطريقة compare(Object o1, Object o2) التي تأخذ وسيطتين من الكائنات، يجب تنفيذها بطريقة تعيد قيمة int سلبية إذا كان الوسيط الأول أصغر من الثاني وتعيد القيمة صفر إذا كانت متساوية وتعيد قيمة int إيجابية إذا كان الوسيط الأول أكبر من الثاني. واجهتا Comparable و Comparator تستخدمان التوابع الجنريكية لفحص الأنواع في وقت الترجمة، تعرف أكثر عن Java Generics. إليك كيف يمكننا إنشاء تنفيذ مختلف لـ Comparator في فئة الموظفين.
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
جميع تنفيذات واجهة Comparator المذكورة أعلاه هي فئات مجهولة. يمكننا استخدام هذه المقارنات كوسيطة لتمرير وسيلة إلى دالة الفرز في فئات Arrays و Collections.
//فرز مصفوفة الموظفين باستخدام Comparator حسب الراتب
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//فرز مصفوفة الموظفين باستخدام Comparator حسب العمر
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//فرز مصفوفة الموظفين باستخدام Comparator حسب الاسم
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
وهنا هو إخراج مقتطف الشفرة أعلاه:
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
لذا الآن نعلم أنه إذا أردنا فرز مصفوفة أو قائمة من كائنات جافا ، فيجب علينا تنفيذ واجهة Comparable لتوفير الترتيب الافتراضي ويجب علينا تنفيذ واجهة Comparator لتوفير طرق مختلفة للترتيب. يمكننا أيضًا إنشاء فئة منفصلة تنفذ واجهة Comparator
ومن ثم استخدامها. وهنا هي الفئات النهائية التي لدينا تشرح Comparable و Comparator في جافا.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//فلنقم بفرز الموظفين بناءً على معرف بترتيب تصاعدي
//يعيد عددًا سالبًا أو صفرًا أو عددًا موجبًا حسب ما إذا كان معرف الموظف هذا
//أقل من أو يساوي أو أكبر من الكائن المحدد.
return (this.id - emp.id);
}
@Override
//هذا مطلوب لطباعة معلومات صديقة للمستخدم حول الموظف
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator SalaryComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator AgeComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator NameComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
}
وهنا هو تنفيذ الفئة المنفصلة لواجهة Comparator التي ستقارن كائنين من موظفين أولاً بناءً على معرفهما وإذا كانوا متساويين ثم بناءً على الاسم.
package com.journaldev.sort;
import java.util.Comparator;
public class EmployeeComparatorByIdAndName implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
int flag = o1.getId() - o2.getId();
if(flag==0) flag = o1.getName().compareTo(o2.getName());
return flag;
}
}
وهنا هو فئة الاختبار حيث نستخدم طرق مختلفة لفرز الكائنات في جافا باستخدام Comparable و Comparator.
package com.journaldev.sort;
import java.util.Arrays;
public class JavaObjectSorting {
/**
* This class shows how to sort custom objects array/list
* implementing Comparable and Comparator interfaces
* @param args
*/
public static void main(String[] args) {
//فرز مصفوفة كائنات مخصصة
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//فرز مصفوفة الموظفين باستخدام تنفيذ واجهة Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
//فرز مصفوفة الموظفين باستخدام Comparator حسب الراتب
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//فرز مصفوفة الموظفين باستخدام Comparator حسب العمر
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//فرز مصفوفة الموظفين باستخدام Comparator حسب الاسم
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
//قائمة الموظفين مرتبة حسب الهوية ثم الاسم باستخدام فئة Comparator
empArr[0] = new Employee(1, "Mikey", 25, 10000);
Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
}
}
إليك نتيجة البرنامج السابق:
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]
واجهتا java.lang.Comparable و java.util.Comparator هما واجهتان قويتان يمكن استخدامهما لتوفير ترتيب الكائنات في جافا.
Comparable vs Comparator
- يمكن استخدام واجهة Comparable لتوفير طريقة واحدة للترتيب بينما تُستخدم واجهة Comparator لتوفير طرق مختلفة للترتيب.
- بالنسبة لاستخدام Comparable، يحتاج الفئة إلى تنفيذها بينما لا نحتاج إلى إجراء أي تغيير في الفئة لاستخدام Comparator.
- واجهة Comparable موجودة في حزمة
java.lang
بينما توجد واجهة Comparator في حزمةjava.util
. - لا نحتاج إلى إجراء أي تغييرات في الشفرة في جانب العميل لاستخدام Comparable، فإن طرق
Arrays.sort()
أوCollection.sort()
تستخدم تلقائيًا طريقةcompareTo()
في الفئة. بالنسبة للمقارن، يحتاج العميل إلى توفير فئة المقارن لاستخدامها في طريقة compare().
هل تعلم أن طريقة Collections.sort() التي تأخذ معامل Comparator تتبع نمط الـ Strategy Pattern؟
يمكنك التحقق من الشيفرة الكاملة وأمثلة أخرى لجافا الأساسية من مستودعنا على جيثب.
Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example