Java示例中的Comparable和Comparator

Comparable 和 Comparator 在 Java 中对于排序对象集合非常有用。Java 提供了一些内置方法,用于对原始类型数组、包装类数组或列表进行排序。首先,我们将学习如何对原始类型和包装类的数组/列表进行排序,然后我们将使用 java.lang.Comparablejava.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 接口的实现对员工数组进行排序
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)方法,该方法由排序方法使用,您可以检查任何Wrapper、String或Date类来确认这一点。我们应该以这样的方式重写此方法,使其返回负整数、零或正整数,以指示“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
    //这是为了打印有关Employee的用户友好信息
    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]]

正如您所见,Employees数组按照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 类的排序函数。

//使用工资比较器对员工数组进行排序
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 对象数组或列表进行排序,我们需要实现 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 接口的单独类,该类将比较两个 Employee 对象,首先根据它们的 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;
    }

}

这是测试类,在这里我们使用不同的方式使用 Comparable 和 Comparator 对 Java 对象进行排序。

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. 对于使用Comparable,客户端不需要在代码中做任何更改,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