为啥 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<Integer>
而不是TreeMap<Integer>
,因为后者不存在,TreeMaps 总是<Key,Value>
-pairs。顺便说一句,假设的 TreeMap<District, Object>
只有在 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
由它的name
和temperature
和
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 类应该实现可比较的?的主要内容,如果未能解决你的问题,请参考以下文章
为啥当我使用hibernate时这个类应该实现java.io.Serializable?
Enumeration和Iterator是接口类,为啥能产生对象