Java中Comparable 和 Comparator

时间:2020-01-09 10:34:59  来源:igfitidea点击:

Java中的Comparable和Comparator是用于对对象集合进行排序的两个接口。显而易见的问题是,为什么两个接口都执行相同的对象排序任务。在这篇文章中,我们将看到Java中什么是Comparable和Comparator接口,为什么都需要它们以及Java中Comparable和Comparator之间的区别。

Java中的接口Comparable

Java中的可比接口只有一个方法compareTo()。

int compareTo(T o)–此方法将用于调用该方法的对象与作为参数传递的对象进行比较。

根据compareTo()方法的实现,实现此接口的类的对象进行排序。由于类本身是在实现接口,因此Comparable接口提供的排序称为类的自然排序。

可以通过Collections.sort(和Arrays.sort)对存储实现该接口的类的对象的列表(和数组)进行自动排序。实现此接口的对象可以用作排序映射中的键(即TreeMap)或者用作排序集中的元素(即TreeSet),而无需指定Comparator。

Java中的接口Comparator

Java中的比较器接口具有方法compare()。

int compare(T o1,T o2)–比较其两个参数的顺序。当第一个参数小于,等于或者大于第二个参数时,返回负整数,零或者正整数。

比较器接口不是使用比较器接口实现提供的顺序排序的类的一部分。比较器接口被实现为单独的类或者匿名类。

可以将比较器传递给排序方法(例如Collections.sort或者Arrays.sort),以实现对排序顺序的精确控制。

Java Comparable 和 Comparator示例

让我们看一个例子,看看如何使用Comparable和Comparator进行排序,以及为什么都需要它们。

有一个Person类,其字段为firstName,lastName和age。我们想使用firstName + lastName对Person类对象进行排序,以便该类实现Comparable接口,并通过覆盖compareTo()方法为该顺序提供逻辑。

public class Person implements Comparable {
  private String firstName;
  private String lastName;
  private int age;
  Person(String firstName, String lastName, int age){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  @Override
  // For sort order - sort on first name, 
  // if equal then sort on lastname
  public int compareTo(Person o) {
    int comp = this.getFirstName().compareTo(o.getFirstName());
    return comp != 0 ? comp :  this.getLastName().compareTo(o.getLastName());
  }

  @Override
  public String toString() {        
    return getFirstName() + " " + getLastName() + " " + getAge();
  }
}

下一个类用于创建Person类对象,并通过调用Collections.sort()方法进行排序。由于存储在List中的对象已经在实现Comparable接口,因此将使用该实现对对象进行排序。

public class SortDemo {
  public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("William", "Shatner", 60));
    personList.add(new Person("William", "Brooks", 25));
    personList.add(new Person("Persis", "Khambatta", 50));
    personList.add(new Person("James", "Doohan", 70));
    personList.add(new Person("DeForest", "Kelley", 65));

    System.out.println("-- Original List --");
    for(Person person : personList){
     System.out.println("" + person);
    }
    // Sort the list
    Collections.sort(personList);
    System.out.println("--Sorted List--");
    for(Person person : personList){
      System.out.println("" + person);
    } 
  }
}

输出:

-- Original List -
William Shatner 60
William Brooks 25
Persis Khambatta 50
James Doohan 70
DeForest Kelley 65
--Sorted List-
DeForest Kelley 65
James Doohan 70
Persis Khambatta 50
William Brooks 25
William Shatner 60

到目前为止,我们已经有了自己的班级,班级的自然排序也可以通过实现Comparable接口来定义。

现在,假设我们有一个新要求,即按年龄或者姓氏和名字对Person类进行排序。 Person类已经与Comparable接口以及该接口施加的顺序耦合在一起。在这种情况下,我们可以使用Comparator接口,因为Comparator可以由另一个类实现,因此我们可以为不同的排序顺序创建不同的Comparator类。然后,我们可以使用Collections.sort()方法,其中还将传递Comparator来对对象进行排序。

public class Person implements Comparable<Person> {
  private String firstName;
  private String lastName;
  private int age;
  Person(String firstName, String lastName, int age){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  @Override
  // For sort order - sort on first name, 
  // if equal then sort on lastname
  public int compareTo(Person o) {
    int comp = this.getFirstName().compareTo(o.getFirstName());
    return comp != 0 ? comp :  this.getLastName().compareTo(o.getLastName());
  }

  @Override
  public String toString() {        
    return getFirstName() + " " + getLastName() + " " + getAge();
  }
}

// Sorting using age field
class AgeComparator implements Comparator<Person>{
  @Override
  public int compare(Person o1, Person o2) {
    return o1.getAge() >= (o2.getAge()) ? 1 : -1;    
  }    
}
//Sorting using last name if equal then sort on firstName
class NameComparator implements Comparator<Person>{
  @Override
  public int compare(Person o1, Person o2) {
    int comp = o1.getLastName().compareTo(o2.getLastName());
    if(comp == 0){
      comp = o1.getFirstName().compareTo(o2.getFirstName());
    }
    return comp;
  }    
}

如我们所见,添加了两个新的Comparator类,一个按年龄排序,另一个按姓氏排序。 Person类仍然具有其Comparable接口实现,以定义其自然顺序。

public class SortDemo {
  public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("William", "Shatner", 60));
    personList.add(new Person("William", "Brooks", 25));
    personList.add(new Person("Persis", "Khambatta", 50));
    personList.add(new Person("James", "Doohan", 70));
    personList.add(new Person("DeForest", "Kelley", 65));
       
    System.out.println("-- Original List --");
    for(Person person : personList){
     System.out.println("" + person);
    }
		 
    // Sort the list by age
    Collections.sort(personList, new AgeComparator());
    System.out.println("--Sorted List by Age--");
    for(Person person : personList){
     System.out.println("" + person);
    } 
     
    // Sort the list by last name
    Collections.sort(personList, new NameComparator());
    System.out.println("--Sorted List by Last Name--");
    for(Person person : personList){
     System.out.println("" + person);
    } 
		 
    // Sort the list by first name - Natural ordering
    Collections.sort(personList);
    System.out.println("--Sorted List by first name--");
    for(Person person : personList){
      System.out.println("" + person);
    } 
  }
}

输出:

-- Original List -
William Shatner 60
William Brooks 25
Persis Khambatta 50
James Doohan 70
DeForest Kelley 65
--Sorted List by Age-
William Brooks 25
Persis Khambatta 50
William Shatner 60
DeForest Kelley 65
James Doohan 70
--Sorted List by Last Name-
William Brooks 25
James Doohan 70
DeForest Kelley 65
Persis Khambatta 50
William Shatner 60
--Sorted List by first name-
DeForest Kelley 65
James Doohan 70
Persis Khambatta 50
William Brooks 25
William Shatner 60

如我们现在所看到的,如果要按年龄排序,则可以在Collections.sort()方法中传递按年龄排序的比较器。如果要按lastName字段进行排序,则采用相同的方法,则可以在Collections.sort()方法中传递按lastName排序的Comparator。如果要按firstName排序,则仍然具有Comparable接口的实现所提供的实现。

Java中Comparable和Comparator之间的区别

ComparableComparator
比较接口具有用于提供排序逻辑的compareTo()</ strong>方法。比较器接口具有用于提供排序逻辑的compare()</ strong>方法。
比较接口在java.lang包中。比较器接口在java.util包中。
比较接口由要对其对象进行排序的类实现。比较器接口由某些其他类或者作为匿名类实现。
由于要对对象进行排序的类实现了Comparable接口,因此Comparable只能提供一种对对象进行排序的方法。可以有许多Comparator类为不同的排序顺序提供逻辑。
具有实现Comparable接口的对象的列表或者数组可以通过仅将Collections.sort()中的List传递或者通过对数组使用Arrays.sort()来进行排序。对于Comparator,必须传递Comparator的实例使用Collections.sort()方法。