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

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

【问题讨论】:

标签: java


【解决方案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 中的相应章节,我还在学习中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-01
        • 1970-01-01
        • 2012-01-28
        • 2017-03-09
        • 2018-05-27
        相关资源
        最近更新 更多