Java中的Comparable和Comparator

时间:2020-02-23 14:36:10  来源:igfitidea点击:

Java中的Comparable和Comparator对排序对象集合非常有用。
Java提供了一些内置方法来对原始类型数组或者包装器类数组或者列表进行排序。
其中我们将首先学习如何对基本类型和包装器类的数组/列表进行排序,然后将使用java.lang.Comparable和java.util.Comparator接口对自定义类的数组/列表进行排序。

让我们看看如何使用简单的程序对基本类型或者对象数组进行排序并列出列表。

package com.theitroad.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) {
      //sort primitives array like int array
      int[] intArr = {5,9,1,10};
      Arrays.sort(intArr);
      System.out.println(Arrays.toString(intArr));
      
      //sorting String array
      String[] strArr = {"A", "C", "B", "Z", "E"};
      Arrays.sort(strArr);
      System.out.println(Arrays.toString(strArr));
      
      //sorting list of objects of Wrapper classes
      List<String> strList = new ArrayList<String>();
      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.theitroad.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
  //this is overridden to print the user-friendly information about the Employee
  public String toString() {
      return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
              this.salary + "]";
  }

}

这是我用来对Employee对象数组进行排序的代码。

//sorting object array
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);

//sorting employees array using Comparable interface implementation
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));

当我尝试运行此程序时,它将引发以下运行时异常。

Exception in thread "main" java.lang.ClassCastException: com.theitroad.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.theitroad.sort.JavaSorting.main(JavaSorting.java:41)

可比和比较器

Java提供了Comparable接口,如果我们想使用Arrays或者Collections排序方法,则可以由任何自定义类实现。

Comparable接口具有compareTo(T obj)方法,该方法由排序方法使用,您可以检查任何Wrapper,String或者Date类来确认这一点。
如果"此"对象小于,等于或者大于作为参数传递的对象,则应以使其返回负整数,零或者正整数的方式覆盖此方法。

在Employee类中实现Comparable接口之后,这里是生成的Employee类。

package com.theitroad.sort;

import java.util.Comparator;

public class Employee implements Comparable<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 int compareTo(Employee emp) {
      //let's sort the employee based on an id in ascending order
      //returns a negative integer, zero, or a positive integer as this employee id
      //is less than, equal to, or greater than the specified object.
      return (this.id - emp.id);
  }

  @Override
  //this is required to print the user-friendly information about the Employee
  public String toString() {
      return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
              this.salary + "]";
  }

}

现在,当我们对Employees进行数组排序并执行上面的代码片段并将其打印时,这是输出。

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升序排列。

但是,在大多数实际场景中,我们希望基于不同的参数进行排序。
例如,作为首席执行官,我想根据工资对员工进行排序,而人力资源则想根据年龄对员工进行排序。
在这种情况下,我们需要使用Java Comparator接口,因为Comparable.compareTo(Object o)方法的实现可以提供默认排序,并且我们不能动态更改它。
而使用Comparator,我们可以定义具有不同排序方式的多种方法,然后根据需要选择排序方法。

Java比较器

需要实现比较器接口的compare(Object o1,Object o2)方法,该方法带有两个Object参数,应以这样的方式实现:如果第一个参数小于第二个参数,则返回负int;如果是,则返回零如果第一个参数大于第二个参数,则等于和正整数。

Comparable和Comparator接口使用泛型进行编译时类型检查,了解有关Java泛型的更多信息。

这是我们如何在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 employees array using Comparator by Salary
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));

//sort employees array using Comparator by Age
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));

//sort employees array using Comparator by Name
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.theitroad.sort;

import java.util.Comparator;

public class Employee implements Comparable<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 int compareTo(Employee emp) {
      //let's sort the employee based on an id in ascending order
      //returns a negative integer, zero, or a positive integer as this employee id
      //is less than, equal to, or greater than the specified object.
      return (this.id - emp.id);
  }

  @Override
  //this is required to print the user-friendly information about the 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<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接口的单独类实现,该实现将首先比较两个Employees对象的ID,如果相同,则比较名称。

package com.theitroad.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.theitroad.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) {

      //sorting custom object array
      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);
      
      //sorting employees array using Comparable interface implementation
      Arrays.sort(empArr);
      System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
      
      //sort employees array using Comparator by Salary
      Arrays.sort(empArr, Employee.SalaryComparator);
      System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
      
      //sort employees array using Comparator by Age
      Arrays.sort(empArr, Employee.AgeComparator);
      System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
      
      //sort employees array using Comparator by Name
      Arrays.sort(empArr, Employee.NameComparator);
      System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
      
      //Employees list sorted by ID and then name using Comparator class
      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,Class需要实现它,而使用Comparator,我们不需要对类进行任何更改。

  • 可比较的接口在java.lang包中,而比较器的接口在java.util包中。

  • 使用Comparable时,我们不需要在客户端进行任何代码更改,而Arrays.sort()或者Collection.sort()方法会自动使用该类的compareTo()方法。
    对于Comparator,客户端需要提供Comparator类以在compare()方法中使用。