Comparable ו-Comparator ב-Java הם כלי מאוד שימושיים למיון של אוספי אובייקטים. ב-Java ישנם שיטות מובנות למיון של מערך של טיפוסי יסוד או מערך של Wrapper classes או רשימה. נלמד תחילה כיצד למיין מערך/רשימה של טיפוסי יסוד ו-Wrapper classes, ולאחר מכן נשתמש ב-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));
//מיון רשימה של אובייקטים מטיפוסי Wrapper classes
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 interface במחלקת העובד, הנה מחלקת העובד התוצאה.
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 + "]";
}
}
עכשיו כאשר אנו מפעילים את קטע הקוד למיון של מערכים של עובדים ומדפיסים את זה, הנה הפלט.
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]]
כפי שאתה יכול לראות, מערך העובדים ממויין לפי המזהה בסדר עולה. אך, ברוב התרחישים בחיי האמת, נרצה מיון המבוסס על פרמטרים שונים. לדוגמה, כמנכ"ל, אני רוצה למיין את העובדים לפי המשכורת, מנהל משאבי אנוש רוצה למיין אותם לפי גיל. זהו המצב בו אנו צריכים להשתמש בממשק Comparator של ג'אווה מאחר שמימוש השיטה Comparable.compareTo(Object o) יכול לספק מיון ברירת מחדל ואין באפשרותנו לשנות אותו באופן דינמי. לעומת זאת, עם Comparator, אנו יכולים להגדיר שיטות מרובות עם דרכים שונות של מיון ולאחר מכן לבחור בשיטת המיון בהתאם לדרישותינו.
Comparator של ג'אווה
ממשק Comparator compare(Object o1, Object o2) צריך להיות ממומש שמקבל שני ארגומנטים Object, צריך לממש אותו באופן שמחזיר int שלילי אם הארגומנט הראשון קטן מהשני ומחזיר אפס אם הם שווים ו int חיובי אם הארגומנט הראשון גדול מהשני. ממשקי Comparable ו-Comparator משתמשים בגנריקס לבדיקת סוג בזמן קומפילציה, למד עוד על גנריקס בג'אווה. הנה כיצד אנו יכולים ליצור ממשק Comparator שונה במחלקת Employee.
/**
* 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());
}
};
כל המימושים האמורים של ממשק ההשוואה למחלקת הממשק הם מחלקות אנונימיות. אנו יכולים להשתמש בממשקים אלה כדי להעביר ארגומנט לפונקציית המיון של מערכות ומחלקות אוספים.
// מיין את מערך העובדים באמצעות השוואת שכר
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
// מיין את מערך העובדים באמצעות השוואת גיל
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
// מיין את מערך העובדים באמצעות השוואת שם
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]]
אז כעת אנו יודעים שאם ברצוננו למיין מערך או רשימת עצמי Java, עלינו לממש את ממשק Comparable שיספק מיון ברירת מחדל ועלינו לממש את ממשק Comparator שיספק דרכים שונות למיון. אפשר גם ליצור כיתה נפרדת שמממשת את ממשק Comparator
ולאחר מכן להשתמש בו. כאן הן הכיתות הסופיות שיש לנו המסבירות Comparable ו-Comparator ב-Java.
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());
}
};
}
כאן המימוש של כיתה נפרדת של ממשק ההשוואה שישווה בין שני אובייקטים של עובדים תחילה לפי התעודה שלהם ואם הם זהים, אז לפי השם שלהם.
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;
}
}
כאן היא הכיתת מבחן בה אנו משתמשים בדרכים שונות למיון אובייקטים ב-Java באמצעות 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 הם ממשקים עוצמתיים הניתנים לשימוש למיון אובייקטים ב-Java.
Comparable נגד Comparator
- ממשק Comparable יכול לשמש לסידור יחיד כאשר ממשק Comparator משמש לסידורים שונים.
- כדי להשתמש ב-Comparable, המחלקה צריכה ליישם אותו, בעוד שבמקרה של Comparator אין צורך בשינויים במחלקה.
- ממשק Comparable נמצא בחבילת
java.lang
, בעוד ממשק Comparator קיים בחבילתjava.util
. - אין צורך בשינויי קוד בצד הלקוח כאשר משתמשים ב-Comparable, שיטות
Arrays.sort()
אוCollection.sort()
משתמשות אוטומטית בשיטתcompareTo()
של המחלקה. לגבי Comparator, הלקוח צריך לספק את מחלקת ה-Comparator לשימוש בשיטה compare().
אתה מכיר את השיטה Collections.sort() שמקבלת ארגומנט Comparator? היא עוקבת אחרי תבנית האסטרטגיה?
אתה יכול לבדוק את הקוד המלא ועוד דוגמאות ב-Java הליבה שלנו מתוך מאגר הקוד שלנו ב-GitHub.
Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example