为啥 Java 类应该实现可比较的?

Posted

技术标签:

【中文标题】为啥 Java 类应该实现可比较的?【英文标题】:Why should a Java class implement comparable?为什么 Java 类应该实现可比较的? 【发布时间】:2011-04-12 17:09:21 【问题描述】:

为什么使用 Java Comparable?为什么有人会在课堂上实现Comparable?什么是您需要实现可比较的现实生活示例?

【问题讨论】:

相关:When to use Comparable vs Comparator 和 Sorting an ArrayList。 这里有一个很好的例子:java67.blogspot.com/2012/10/… 【参考方案1】:

这是一个真实的示例。注意String 也实现了Comparable

class Author implements Comparable<Author>
    String firstName;
    String lastName;

    @Override
    public int compareTo(Author other)
        // compareTo should return < 0 if this is supposed to be
        // less than other, > 0 if this is supposed to be greater than 
        // other and 0 if they are supposed to be equal
        int last = this.lastName.compareTo(other.lastName);
        return last == 0 ? this.firstName.compareTo(other.firstName) : last;
    

稍后..

/**
 * List the authors. Sort them by name so it will look good.
 */
public List<Author> listAuthors()
    List<Author> authors = readAuthorsFromFileOrSomething();
    Collections.sort(authors);
    return authors;


/**
 * List unique authors. Sort them by name so it will look good.
 */
public SortedSet<Author> listUniqueAuthors()
    List<Author> authors = readAuthorsFromFileOrSomething();
    return new TreeSet<Author>(authors);

【讨论】:

我只想指出,您经常需要覆盖equals(因此是hashCode)以与您的compareTo 方法保持一致。例如,如果您希望班级与TreeSet 一起玩,这是必要的。 为什么不直接返回last @AnirbanNag'tintinmj' 自动按名字排序,以防姓氏相同。 另外一个很好地解释了为什么 compareTo 返回一个 int 及其含义。最有帮助。 @user3932000:没错,这就是所有接口的重点。但请注意,“其他 Java 方法”包括用户编写的方法!事实上,我声称大多数接口都被用户的代码使用。在更大的代码库中,“你自己”很快就变成了“其他人”【参考方案2】:

Comparable 定义了自然排序。这意味着当一个对象应该被认为是“小于”或“大于”时,您正在定义它。

假设你有一堆整数,你想对它们进行排序。这很简单,只需将它们放在一个排序的集合中,对吗?

TreeSet<Integer> m = new TreeSet<Integer>(); 
m.add(1);
m.add(3);
m.add(2);
for (Integer i : m)
... // values will be sorted

但现在假设我有一些自定义对象,排序对我来说是有意义的,但未定义。假设我有按邮政编码和人口密度表示地区的数据,我想按密度对它们进行排序:

public class District 
  String zipcode; 
  Double populationDensity;

现在对它们进行排序的最简单方法是通过实现 Comparable 以自然排序来定义它们,这意味着这些对象被定义为排序的标准方式。:

public class District implements Comparable<District>
  String zipcode; 
  Double populationDensity;
  public int compareTo(District other)
  
    return populationDensity.compareTo(other.populationDensity);
  

请注意,您可以通过定义比较器来做同样的事情。不同之处在于比较器定义了对象之外的排序逻辑。也许在一个单独的过程中,我需要按邮政编码对相同的对象进行排序——在这种情况下,排序不一定是对象的属性,或者与对象的自然排序不同。您可以使用外部比较器来定义整数的自定义排序,例如按字母值对它们进行排序。

基本上,排序逻辑必须存在于某个地方。那可以-

在对象本身中,如果它是自然可比较的(扩展 Comparable - 例如整数)

在外部比较器中提供,如上例所示。

【讨论】:

很好的例子,但它必须是TreeSet&lt;Integer&gt; 而不是TreeMap&lt;Integer&gt;,因为后者不存在,TreeMaps 总是&lt;Key,Value&gt;-pairs。顺便说一句,假设的 TreeMap&lt;District, Object&gt; 只有在 District 实施 Comparable 时才有效,对吧?仍在尝试理解这一点【参考方案3】:

引用自javadoc;

这个接口强加了一个总 对每个类的对象进行排序 实现它。这个排序是 称为类的自然 排序和类的 compareTo 方法被称为其自然 比较法。

对象的列表(和数组) 实现这个接口可以排序 自动由 Collections.sort (和 Arrays.sort)。 实现的对象 这个接口可以作为key 排序的地图或作为元素 排序集,无需 指定一个比较器。

编辑:..并将重要的部分加粗。

【讨论】:

我想说的是,你加粗的那个之后这句话同样重要(如果不是更重要的话)。【参考方案4】:

类实现Comparable 的事实意味着您可以从该类中获取两个对象并进行比较。某些类,例如某些保持对象有序的集合(集合中的排序函数)依赖于它们的可比性(为了进行排序,您需要知道哪个对象是“最大的”等等)。

【讨论】:

【参考方案5】:

上面的大多数示例都展示了如何在 compareTo 函数中重用现有的可比较对象。如果您想在比较同一类的两个对象时实现自己的 compareTo,比如说您想按价格排序的 AirlineTicket 对象(less 排在第一位),然后是中途停留的次数(同样,less 是排名第一),您将执行以下操作:

class AirlineTicket implements Comparable<Cost>

    public double cost;
    public int stopovers;
    public AirlineTicket(double cost, int stopovers)
    
        this.cost = cost; this.stopovers = stopovers ;
    

    public int compareTo(Cost o)
    
        if(this.cost != o.cost)
          return Double.compare(this.cost, o.cost); //sorting in ascending order. 
        if(this.stopovers != o.stopovers)
          return this.stopovers - o.stopovers; //again, ascending but swap the two if you want descending
        return 0;            
    

【讨论】:

Cost 有一个属性cost 作为AirlineTicket?属性stopovers(在两个类中)也一样吗?很奇怪…… Cost 的实现是什么?【参考方案6】:

实现多字段比较的一种简单方法是使用Guava's ComparisonChain - 然后你可以说

   public int compareTo(Foo that) 
     return ComparisonChain.start()
         .compare(lastName, that.lastName)
         .compare(firstName, that.firstName)
         .compare(zipCode, that.zipCode)
         .result();
   

而不是

  public int compareTo(Person other) 
    int cmp = lastName.compareTo(other.lastName);
    if (cmp != 0) 
      return cmp;
    
    cmp = firstName.compareTo(other.firstName);
    if (cmp != 0) 
      return cmp;
    
    return Integer.compare(zipCode, other.zipCode);
  

【讨论】:

【参考方案7】:

Comparable 用于比较类的实例。我们可以通过多种方式比较实例,这就是为什么我们需要实现一个方法 compareTo 以便了解我们想要如何(属性)比较实例。

Dog类:

package test;
import java.util.Arrays;

public class Main 

    public static void main(String[] args) 
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) 
            System.out.println(dogs[i].getName());
        
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) 
            System.out.println(dogs[i].getName());
        
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    

Main类:

package test;

import java.util.Arrays;

public class Main 

    public static void main(String[] args) 
        Dog d1 = new Dog("brutus");
        Dog d2 = new Dog("medor");
        Dog d3 = new Dog("ara");
        Dog[] dogs = new Dog[3];
        dogs[0] = d1;
        dogs[1] = d2;
        dogs[2] = d3;

        for (int i = 0; i < 3; i++) 
            System.out.println(dogs[i].getName());
        
        /**
         * Output:
         * brutus
         * medor
         * ara
         */

        Arrays.sort(dogs, Dog.NameComparator);
        for (int i = 0; i < 3; i++) 
            System.out.println(dogs[i].getName());
        
        /**
         * Output:
         * ara
         * medor
         * brutus
         */

    

这是一个如何在 Java 中使用可比性的好例子:

http://www.onjava.com/pub/a/onjava/2003/03/12/java_comp.html?page=2

【讨论】:

您只是复制了主类代码库而不是 Dog 类。 Dog.NameComparator 的代码在哪里【参考方案8】:

例如当你想要一个排序的集合或map

【讨论】:

费尔南多的意思是:如果你将实现 Comparable 的“事物”存储在排序容器类中,排序容器类可以自动对这些“事物”进行排序。【参考方案9】:

好的,但为什么不直接定义compareTo() 方法而不实现可比较的接口。 例如一个类City由它的nametemperature

定义
public int compareTo(City theOther)

    if (this.temperature < theOther.temperature)
        return -1;
    else if (this.temperature > theOther.temperature)
        return 1;
    else
        return 0;

【讨论】:

这不起作用。没有实现可比较 - 我得到 classcast 异常【参考方案10】:

实现Comparable接口时,需要实现方法compareTo()。您需要它来比较对象,以便使用例如ArrayList 类的排序方法。您需要一种方法来比较您的对象以便能够对它们进行排序。所以你需要在你的类中自定义一个compareTo() 方法,这样你就可以将它与ArrayList 排序方法一起使用。 compareTo() 方法返回 -1,0,1。

我刚刚阅读了 Java Head 2.0 中的相应章节,我还在学习中。

【讨论】:

以上是关于为啥 Java 类应该实现可比较的?的主要内容,如果未能解决你的问题,请参考以下文章

为啥java Iterator接口应该实现为内部类?

为啥当我使用hibernate时这个类应该实现java.io.Serializable?

java讨论:啥是多态和接口,为啥需要?

Enumeration和Iterator是接口类,为啥能产生对象

为啥 java.util.Stack 是使用 Vector 而不是 Arraylist 实现的

为啥Java中的String类没有实现Iterable?