Comparable接口中的compareTo方法用于实现一些类中的数据排序功能,例如ArrayList,Arrays,TreeSet等类。其中Arrays类需要主动调用sort,之后sort方法将会调用重载好的compareTo方法作为排序规则,之后该类内的数据便会以排序后的数据进行存放,而TreeSet作为一种本身有序的方法,会默认调用compareTo方法来排序。
一、基本使用早期
public interface Comparable
{
int compareTo(Object other)
}
java5版本之后的Comparable接口已经将Comparable接口提升为了一个泛型类型
public interface Comparable
{
int compareTo(T other)
}
因此在实现Comparable的接口的类中,必须提供以下方法
int compareTo(T other)
当然也仍旧可以使用早期的Object参数版本,但是如果使用这种方法必须使用强制类型转换,这也就增加了ClassCastException出现的可能性
以以下这道例题来作为假设:
(1)定义User类(sno,name,password,birthDate,sex,salary)。
(2)要求重写toString()方法和compareTo()方法,用户先按sex再按birthDate排序。
(3)至少生成5个User对象,放入到ArrayList中,并排序输出。
现在, 假设希望使用 ArrayList 类的 sort 方法对 User 对象数组进行排序, User类
就必须实现 Comparable 接口。
为了让类实现一个接口, 通常需要下面两个步骤:
1 ) 将类声明为实现给定的接口。
2 ) 对接口中的所有方法进行定义。
要将类声明为实现某个接口, 需要使用关键字 implements:
class User iipleients Comparable
当然, 这里的 User 类需要提供 compareTo 方法。
import java.io.*; import java.util.ArrayList; public class User implements Comparable{ //注意,再java5中,comparable接口已经提升为一个泛型类型 public int sno; public String name; public String password; public String birthDate; public String sex; //male female public double salary; @Override public String toString() { return "sno=" + sno + " name=" + name + " password=" + password + " birthdate=" + birthDate + " sex=" + sex + " salary=" + salary; } @Override public int compareTo(User user){ if(this.sex.compareTo(user.sex)<0){ //说明此时是女的和男的比 return -1; //这里调用的是String类型的compareTo } //还有Double.compareTo等等类型比较 else if(this.sex.compareTo(user.sex)==0){ //女的和女的或男的和男的 if(this.sex.compareTo("female")==0){ return this.birthDate.compareTo(user.birthDate); }//此时是女生与女生比,让生日小的再前面,大的在后面 else{ return this.birthDate.compareTo(user.birthDate); } } else if(this.sex.compareTo(user.sex)>0){//此处说明是男的和女的比 return 1; //让男的再后面 } return 0; } public User() { } public User(String name, String password, String birthDate, String sex, double salary,int sno) { this.sno = sno; this.name = name; this.password = password; this.birthDate = birthDate; this.sex = sex; this.salary = salary; } public static void main(String[] args) throws IOException, ClassNotFoundException { ArrayList arrayList=new ArrayList (); arrayList.add(new User("zjb","1","0114","male",123.2,1)); arrayList.add(new User("hyl","2","0625","female",123.2,2)); arrayList.add(new User("zb","3","0707","male",123.2,3)); arrayList.add(new User("yl","4","0617","female",123.2,4)); arrayList.add(new User("love","5","0520","female",123.2,5)); arrayList.sort(User::compareTo); for(int i=0;i<5;i++) { System.out.println(arrayList.get(i)); } }
运行结果如下,在main函数中显式调用sort并且放入我们从写好的compareTo方法对ArrayList类对象进行排序。
compareTo返回值一般为-1,0,1,以及如果两个对象无法比较,那么会抛出异常,且返回值为-1的放在上面,为1的在下面,也就是按照升序排序。
接下来是一些可能出现的问题以及可能出现的疑惑的解答
语 言 标 准 规 定:对 于 任 意 的 x 和 y, 实 现 必 须 能 够 保 证 sgn(x.compareTo(y)) = -sgn
(y.compareTo(x)。) (也 就 是 说, 如 果 y.compareTo(x) 抛 出 一 个 异 常, 那 么 x.compareTo(y) 也 应 该 抛 出 一 个 异 常。)这 里 的“ sgn” 是 一 个 数 值 的 符 号:如 果 n 是 负 值, sgn(n) 等 于 -1 ; 如 果 n 是 0, sgn(n) 等 于 0 ; 如 果 n 是 正 值, sgn(n)等 于 1 简 单 地 讲, 如 果 调 换compareTo 的 参 数, 结 果 的 符 号 也 应 该 调 换 (而 不 是 实 际 值) 与 equals 方 法 一 样, 在 继 承 过 程 中 有 可 能 会 出 现 问 题。
例如由于父类实现了Comparable,而子类并没有,那么我们并不能按照比较父类对象的方法来比较子类,这是由于我们不可能强制的将子类对象转换为父类对象,这有可能出现异常。
如 果 子 类 之 间 的 比 较 含 义 不 一 样, 那 就 属 于 不 同 类 对 象 的 非 法 比 较。因此, 每 个compareTo 方 法 都 应 该 在 开 始 时 进 行 下 列 检 测:
if (getClassO != other.getClassO) throw new ClassCastExceptionO;
如 果 存 在 这 样 一 种 通 用 算 法, 它 能 够 对 两 个 不 同 的 子 类 对 象 进 行 比 较, 则 应 该 在 超类 中 提 供 一 个 compareTo 方 法,并 将 这 个 方 法 声 明 为 final 。
对于Arrays类中的sort方法,其中可能的实现过程如下
if(a[i].compareTo(a[j])>0)
{
//rerange
}
也就是编译器必须确认a[i]一定有一个方法compareTo,因此如果a是一个Comparable对象的数组,就可以确保拥有compareTo方法,因为每个实现Comparable接口的类都必须提供这个方法的定义。
Comparator又人认为,将Arrays类中的sort方法定义为接收一个Comparable[ ]数组,倘若有人调用sort方法时所提供的数组元素类型没有实现Comparable方法,编译器就会给出错误报告。但事实并非如此。在这种情况下,sort方法可以接收一个 Object[ ]数组,并对其进行笨拙的类型转换:
// Approach used in the standard library–not recommendedif (((Comparable) a[i]).compareTo(a[j])>0)
{
l/rearrange a[i] and alj]
如果a[i]不属于实现了Comparable接口的类,那么虚拟机就会抛出一个异常。
Comparator一般用于我们不能使用compareTo方法来比较两个对象的时候,例如compareTo默认比较的是两个String对象中所在字典的先后有顺序,那么如果我们需要比较的是字符串的长度,我们就需要将String类的compareTo方法重载为比较两个字符串的长度,那么这种出现两种compareTo的情况我们应该避免,因此Java提供了Comparator(比较器)接口。
public interface Comparator
{
int compare(T o1,T o2)
}
package Learning.InterfaceDemo; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class ComparatorDemo implements Comparator{ //注意,再java5中,comparable接口已经提升为一个泛型类型 public int sno; public String name; public String password; public String birthDate; public String sex; //male female public double salary; @Override public String toString() { return "sno=" + sno + " name=" + name + " password=" + password + " birthdate=" + birthDate + " sex=" + sex + " salary=" + salary; } @Override public int compare(ComparatorDemo user1, ComparatorDemo user2) { if (user1.sex.compareTo(user2.sex) < 0) { //说明此时是女的和男的比 return -1; //这里调用的是String类型的compareTo } //还有Double.compareTo等等类型比较 else if (user1.sex.compareTo(user2.sex) == 0) { //女的和女的或男的和男的 if (user1.sex.compareTo("female") == 0) { return user1.birthDate.compareTo(user2.birthDate); }//此时是女生与女生比,让生日小的再前面,大的在后面 else { return user1.birthDate.compareTo(user2.birthDate); } } else if (user1.sex.compareTo(user2.sex) > 0) {//此处说明是男的和女的比 return 1; //让男的再后面 } return 0; } public ComparatorDemo() { } public ComparatorDemo(String name, String password, String birthDate, String sex, double salary, int sno) { this.sno = sno; this.name = name; this.password = password; this.birthDate = birthDate; this.sex = sex; this.salary = salary; } public static void main(String[] args) throws IOException, ClassNotFoundException { ArrayList arrayList = new ArrayList<>(); arrayList.add(new ComparatorDemo("zjb", "1", "0114", "male", 123.2, 1)); arrayList.add(new ComparatorDemo("hyl", "2", "0625", "female", 123.2, 2)); arrayList.add(new ComparatorDemo("zb", "3", "0707", "male", 123.2, 3)); arrayList.add(new ComparatorDemo("yl", "4", "0617", "female", 123.2, 4)); arrayList.add(new ComparatorDemo("love", "5", "0520", "female", 123.2, 5)); Collections.sort(arrayList, new ComparatorDemo()); for (int i = 0; i < 5; i++) { System.out.println(arrayList.get(i)); } } }
与Comparable接口类似,Comparator接口只不过是将隐式参数作为了compare方法中的显示参数,也就是原本的this变成了参数中的 T o1,如果不知道什么是隐式和显式参数我现在介绍一下,对于一个方法的调用,如a.sort(b),那么a就是这个方法的隐式参数,b是这个方法的显式参数,真正使用这个方法的时候,this就代表的是调用者a。
如上使用compareTo的方法中this.compareTo(user,sex)类似,compare方法只不过是直接将要比较的两个对象作为参数,compare(User user1,User user2),且调用者变为了比较器对象,例如Collections或Arrays等。
疑问解答
虽然User对象没有状态,不过还是需要建立这个对象的一个实例,我们需要这个实例来调用compare方法,因为这个compare方法并不是一个静态方法!!



