Java6.0中Comparable接口与Comparator接口详解

Part I

Comparable与Comparator接口不仅在串法上相似,而且他们的作用都十分的相似,他们可以让对象实现可排序。

首先让我们看看官方文档(JDK6.0 API)中对他们的描述:

public interface Comparable<T>

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's compareTo method is referred to as its natural comparison method.

public interface Comparator<T>

A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such asCollections.sort or Arrays.sort) to allow precise control over the sort order. Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.

从上描述可以知道,Comparable是让实体类去实现的,而实现了这个接口的实体类的本身就具备了对象间的可比较性。

而实现Comparator接口的类则是一个单纯可比较算法的实现。

 

Part II

下面是一个Person的实体类定义:

   1: public class Person {
   2:     
   3:     private String name;
   4:     private int age;
   5:     
   6:     public String getName() {
   7:         return name;
   8:     }
   9:     
  10:     public void setName(String name) {
  11:         this.name = name;
  12:     }
  13:     
  14:     public int getAge() {
  15:         return age;
  16:     }
  17:     
  18:     public void setAge(int age) {
  19:         this.age = age;
  20:     }
  21:     
  22: }

 

那如何让Person的对象根据name来实现对比呢?首先我们尝试一下在对象间直接使用逻辑运算符进行操作:

   1: public class CompareTest {
   2:     
   3:     public static void main(String[] args) {
   4:         Person p1 = new Person();
   5:         p1.setName("P1");
   6:         p1.setAge(20);
   7:         
   8:         Person p2 = new Person();
   9:         p2.setName("P2");
  10:         p2.setAge(30);
  11:         
  12:         System.out.println(p1 > p2);
  13:     }
  14:     
  15: }

结果如下:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    The operator > is undefined for the argument type(s) Person, Person

    at CompareTest.main(CompareTest.java:12)

看来直接用逻辑运算符是没办法判断两个对象大小,其实自己想想也知道,如果用逻辑运算符来判断两个对象的大小,那应该用对象的什么来作为判断条件呢?名字?年龄?hashCode值?内存地址?

 

Part III

接下来让我们看看Comparable和Comparator的强大功力:

   1: public class Person implements Comparable<Person> {
   2:  
   3:     private String name;
   4:     private int age;
   5:  
   6:     public String getName() {
   7:         return name;
   8:     }
   9:  
  10:     public void setName(String name) {
  11:         this.name = name;
  12:     }
  13:  
  14:     public int getAge() {
  15:         return age;
  16:     }
  17:  
  18:     public void setAge(int age) {
  19:         this.age = age;
  20:     }
  21:  
  22:     @Override
  23:     public int compareTo(Person p) {
  24:         if (this.age > p.age) {
  25:             return 1;
  26:         } else if (this.age < p.age) {
  27:             return -1;
  28:         }
  29:         return 0;
  30:     }
  31:  
  32: }
   1: public class CompareTest {
   2:  
   3:     public static void main(String[] args) {
   4:         Person p1 = new Person();
   5:         p1.setName("P1");
   6:         p1.setAge(20);
   7:  
   8:         Person p2 = new Person();
   9:         p2.setName("P2");
  10:         p2.setAge(30);
  11:  
  12:         System.out.println(p1.compareTo(p2));
  13:     }
  14:  
  15: }

运行的结果为:-1

读者看到这里一定会觉得很好笑,我不就实现了一个接口重写了个方法吗?然后又调用这个方法,而这个方法的返回值又是我自己定义的?那实现这个接口有什么意义呢?不实现这个接口我也可以给他一个compareTo的方法啊?而且对运行的结果没有影响!

没错,在这里确实还不能体现出Comparable接口的作用,但是别忘了我们的强大的Java类库,实现了Comparable接口的类当然是要结合类库中的其他类来使用!

下面我们先对Person类进行一下处理,添加了带参的构造器并重写了toString()方法,方便演示程序的运行结果:

   1: public class Person implements Comparable<Person> {
   2:  
   3:     private String name;
   4:     private int age;
   5:     
   6:     public Person() {
   7:         
   8:     }
   9:     
  10:     public Person(String name, int age) {
  11:         this.name = name;
  12:         this.age = age;
  13:     }
  14:  
  15:     public String getName() {
  16:         return name;
  17:     }
  18:  
  19:     public void setName(String name) {
  20:         this.name = name;
  21:     }
  22:  
  23:     public int getAge() {
  24:         return age;
  25:     }
  26:  
  27:     public void setAge(int age) {
  28:         this.age = age;
  29:     }
  30:  
  31:     @Override
  32:     public int compareTo(Person p) {
  33:         if (this.age > p.age) {
  34:             return 1;
  35:         } else if (this.age < p.age) {
  36:             return -1;
  37:         }
  38:         return 0;
  39:     }
  40:  
  41:     @Override
  42:     public String toString() {
  43:         return "Person [age=" + age + ", name=" + name + "]";
  44:     }
  45:  
  46: }

接下来我们定义一个无序的Person对象数组persons,并使用java.utils.Arrays工具类对其进行排序:

   1: import java.util.Arrays;
   2:  
   3: public class CompareTest {
   4:  
   5:     public static void main(String[] args) {
   6:         Person[] persons = {
   7:                 new Person("P1", 60),
   8:                 new Person("P2", 20),
   9:                 new Person("P3", 40)
  10:         };
  11:         
  12:         System.out.println(Arrays.toString(persons));
  13:         
  14:         Arrays.sort(persons);
  15:         
  16:         System.out.println(Arrays.toString(persons));
  17:     }
  18:  
  19: }

结果如下:

[Person [age=60, name=P1], Person [age=20, name=P2], Person [age=40, name=P3]]
[Person [age=20, name=P2], Person [age=40, name=P3], Person [age=60, name=P1]]

没错,无序的对象数组进过处理后按照age的大小来进行排序,这就是实现Comparable接口所带来的效果。读者如果对此有疑惑,可以尝试不实现Comparable接口,来进行排序。


Part IV

说到现在,读者应该对Comparable接口有了大概的了解,但是为什么又要有一个Comparator接口呢?难道Java的开发者都吃饱撑着没事做吗?

再谈Comparator接口之前,大家应该先了解一个叫“策略模式”的东东。一下是百度百科对策略模式的描写:

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)

之所以要谈到策略模式,就是因为Comparator接口其实就是一种策略模式的实践。实现Comparator接口的类必然就会实现一个compareTo(Object o1, Object o2)的方法,而这个方法就是算法中的一部分,所有使用了compareTo方法的类都不会关心compareTo是如何工作的,只关心他的返回值,这也是面向对象中著名的封装特性。

那Comparator接口应该如何使用呢?别急,首先我们要先对Person类进行一下处理,因为我们现在使用Comparator接口,所以Comparable接口就可以光荣的退休了:

   1: public class Person {
   2:  
   3:     private String name;
   4:     private int age;
   5:     
   6:     public Person() {
   7:         
   8:     }
   9:     
  10:     public Person(String name, int age) {
  11:         this.name = name;
  12:         this.age = age;
  13:     }
  14:  
  15:     public String getName() {
  16:         return name;
  17:     }
  18:  
  19:     public void setName(String name) {
  20:         this.name = name;
  21:     }
  22:  
  23:     public int getAge() {
  24:         return age;
  25:     }
  26:  
  27:     public void setAge(int age) {
  28:         this.age = age;
  29:     }
  30:  
  31:     @Override
  32:     public String toString() {
  33:         return "Person [age=" + age + ", name=" + name + "]";
  34:     }
  35:  
  36: }

新建一个实现Comparator的实现类PersonComparator:

   1: import java.util.Comparator;
   2:  
   3: public class PersonComparator implements Comparator<Person> {
   4:  
   5:     @Override
   6:     public int compare(Person p1, Person p2) {
   7:         if (p1.getAge() > p2.getAge()) {
   8:             return 1;
   9:         } else if (p1.getAge() < p2.getAge()) {
  10:             return -1;
  11:         }
  12:         return 0;
  13:     }
  14:     
  15: }

然后再用回我们的CompareTest做测试:

   1: import java.util.Arrays;
   2:  
   3: public class CompareTest {
   4:  
   5:     public static void main(String[] args) {
   6:         Person[] persons = {
   7:                 new Person("P1", 60),
   8:                 new Person("P2", 20),
   9:                 new Person("P3", 40)
  10:         };
  11:         
  12:         System.out.println(Arrays.toString(persons));
  13:         
  14:         Arrays.sort(persons, new PersonComparator());
  15:         
  16:         System.out.println(Arrays.toString(persons));
  17:     }
  18:  
  19: }

注意:在上面的sort方法中,我们传进了一个PersonComparator的实例对象作为参数,读者也可以试试不传入这个参数,看是否能正确工作。

结果当然是跟上面演示的例子一样啦,我就不再重复了。

 

Part V

Comparable与Comparator的作用很类似,那到底应该有哪个呢?

这个问题留给读者去思考,在这里我没有这个资格做出评论,只是我在很多的讨论中都了解到用Comparator接口的人比较多。

最后是东方标准杨帆老师使用Comparator的一点心得,谢谢杨老师的教导:

   1: import java.util.Comparator;
   2:  
   3: public class Person {
   4:  
   5:     private String name;
   6:     private int age;
   7:     
   8:     public Person() {
   9:     }
  10:     
  11:     public Person(String name, int age) {
  12:         this.name = name;
  13:         this.age = age;
  14:     }
  15:  
  16:     public String getName() {
  17:         return name;
  18:     }
  19:  
  20:     public void setName(String name) {
  21:         this.name = name;
  22:     }
  23:  
  24:     public int getAge() {
  25:         return age;
  26:     }
  27:  
  28:     public void setAge(int age) {
  29:         this.age = age;
  30:     }
  31:  
  32:     @Override
  33:     public String toString() {
  34:         return "Person [age=" + age + ", name=" + name + "]";
  35:     }
  36:     
  37:     public static final Comparator<Person> PERSON_COMPARATOR = new Comparator<Person>() {
  38:         
  39:         @Override
  40:         public int compare(Person p1, Person p2) {
  41:             if (p1.getAge() > p2.getAge()) {
  42:                 return 1;
  43:             } else if (p1.getAge() < p2.getAge()) {
  44:                 return -1;
  45:             }
  46:             return 0;
  47:         }
  48:     };
  49:  
  50: }

这里定义了一个PERSON_COMPARATOR的静态内部类。

使用方法如下:

   1: import java.util.Arrays;
   2:  
   3: public class CompareTest {
   4:  
   5:     public static void main(String[] args) {
   6:         Person[] persons = {
   7:                 new Person("P1", 60),
   8:                 new Person("P2", 20),
   9:                 new Person("P3", 40)
  10:         };
  11:         
  12:         System.out.println(Arrays.toString(persons));
  13:         
  14:         Arrays.sort(persons, Person.PERSON_COMPARATOR);
  15:         
  16:         System.out.println(Arrays.toString(persons));
  17:     }
  18:  
  19: }

为什么要这样定义了,因为从上述的例子就可以看出,我们只是调用了这个静态内部类作为方法的参数,而这个静态内部类只会在类加载的时候被实例化一次,换句话说在同一个运行环境中只存在一个这样的实例对象,而像Part IV的写法会产生很多的对象实例。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值