Java示例中的Comparable和Comparator

在Java中,Comparable和Comparator對於排序對象的集合非常有用。Java提供了一些內置方法,可以對基本類型數組、包裝類數組或列表進行排序。首先,我們將學習如何對基本類型和包裝類的數組/列表進行排序,然後使用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
    //此方法被覆寫以打印有關Employee的用戶友好信息
    public String toString() {
        return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
                this.salary + "]";
    }

}

這是我用於對Employee對象數組進行排序的代碼。

//排序對象數組
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接口實現對Employee數組進行排序
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

Java 提供 Comparable 接口,如果我們想要使用 ArraysCollections 排序方法,則任何自定義類別都應該實現該接口。Comparable 接口具有 compareTo(T obj) 方法,該方法被排序方法使用,您可以檢查任何包裝類、字符串或日期類來確認這一點。我們應該以一種方式覆蓋此方法,以便它在“this”對象小於、等於或大於作為參數傳遞的對象時返回負整數、零或正整數。在 Employee 類中實現 Comparable 接口 後,這是生成的 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) {
        //根據 id 升序排序員工
        //當作為參數傳遞的對象
        //返回一個負整數、零或正整數,表示此員工 id
        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]]

你可以看到,員工陣列是按照 id 遞增的方式排序的。但是,在大多數實際場景中,我們希望根據不同的參數進行排序。例如,作為 CEO,我希望根據薪水來排序員工,HR 希望根據年齡來排序他們。這就是我們需要使用 Java Comparator 接口的情況,因為 Comparable.compareTo(Object o) 方法的實現可以提供默認排序,而我們無法動態更改它。然而,使用 Comparator,我們可以定義多個不同排序方式的方法,然後根據我們的需求選擇排序方法。

Java Comparator

Comparator 接口需要實現 compare(Object o1, Object o2) 方法,該方法接受兩個 Object 參數,應該這樣實現:如果第一個參數小於第二個參數,則返回負整數;如果它們相等,則返回零;如果第一個參數大於第二個參數,則返回正整數。Comparable 和 Comparator 接口使用泛型進行編譯時類型檢查,了解更多關於 Java Generics。以下是如何在 Employee 類中創建不同的 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类的sort函数。

//使用工资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]]

现在我们知道,如果要对Java对象数组或列表进行排序,我们需要实现Java Comparable接口以提供默认排序,并且我们应该实现Java Comparator接口以提供不同的排序方式。我们还可以创建一个实现Comparator接口的单独类,然后使用它。这里是我们在Java中解释的最终类,涵盖了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) {
        //根据升序的id对员工进行排序
        //返回一个负整数,零,或正整数,表示此员工id
        //小于,等于或大于指定对象。
        return (this.id - emp.id);
    }

    @Override
    //这是打印有关Employee的用户友好信息所必需的
    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接口的单独类实现,它将比较两个Employees对象首先按照它们的id,如果相同,则按照姓名。

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类按ID和名称排序的员工列表
        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.Comparablejava.util.Comparator是强大的接口,可用于在Java中对对象进行排序。

Comparable vs Comparator

  1. Comparable接口可用于提供单一排序方式,而Comparator接口用于提供不同的排序方式。
  2. 使用Comparable时,类需要实现它,而使用Comparator时,我们无需对类进行任何更改。
  3. Comparable接口位于java.lang包中,而Comparator接口位于java.util包中。
  4. 我们无需在客户端进行任何代码更改,Arrays.sort()Collection.sort()方法会自动使用类的compareTo()方法进行排序。对于Comparator,客户端需要提供Comparator类以在compare()方法中使用。

你知道Collections.sort()方法接受Comparator参数,遵循策略模式吗?

你可以从我们的GitHub代码库中检查完整代码和更多核心Java示例。

Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example