天天看点

深入java--comparable接口和comparator接口比较java中compareTo和compare方法之比较

对于comparable接口和comparator接口比较在网上搜索了一些文章,现在做以下总结归纳:

一:

Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。

Comparator位于包java.util下,而Comparable位于包   java.lang下

Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口)   

 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序,如API所说:

Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface

这里的自然顺序就是实现Comparable接口设定的排序方式。

而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。

可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。

用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。

// AbsComparator.java     
import   java.util.*;

public   class   AbsComparator   implements   Comparator   {     
    public   int   compare(Object   o1,   Object   o2)   {     
      int   v1   =   Math.abs(((Integer)o1).intValue());     
      int   v2   =   Math.abs(((Integer)o2).intValue());     
      return   v1   >   v2   ?   1   :   (v1   ==   v2   ?   0   :   -1);     
    }     
}
           

可以用下面这个类测试 AbsComparator:     

// Test.java     
import   java.util.*;     

public   class   Test   {     
    public   static   void   main(String[]   args)   {     
    
      //产生一个20个随机整数的数组(有正有负)     
      Random   rnd   =   new   Random();     
      Integer[]   integers   =   new   Integer[20];     
      for(int   i   =   0;   i   <   integers.length;   i++)     
      integers[i]   =   new   Integer(rnd.nextInt(100)   *   (rnd.nextBoolean()   ?   1   :   -1));     
    
      System.out.println("用Integer内置方法排序:");     
      Arrays.sort(integers);     
      System.out.println(Arrays.asList(integers));     
    
      System.out.println("用AbsComparator排序:");     
      Arrays.sort(integers,   new   AbsComparator());     
      System.out.println(Arrays.asList(integers));     
    }     
}
           

 Collections.sort((List<T> list, Comparator<? super T> c)是用来对list排序的。

如果不是调用sort方法,相要直接比较两个对象的大小,如下:

Comparator定义了俩个方法,分别是   int   compare(T   o1,   T   o2)和   boolean   equals(Object   obj),

用于比较两个Comparator是否相等

true only if the specified object is also a comparator and it imposes the same ordering as this comparator.

有时在实现Comparator接口时,并没有实现equals方法,可程序并没有报错,原因是实现该接口的类也是Object类的子类,而Object类已经实现了equals方法

 Comparable接口只提供了   int   compareTo(T   o)方法,也就是说假如我定义了一个Person类,这个类实现了   Comparable接口,那么当我实例化Person类的person1后,我想比较person1和一个现有的Person对象person2的大小时,我就可以这样来调用:person1.comparTo(person2),通过返回值就可以判断了;而此时如果你定义了一个   PersonComparator(实现了Comparator接口)的话,那你就可以这样:PersonComparator   comparator=   new   PersonComparator();

comparator.compare(person1,person2);。

二:

1.回答得到的返回值是负整数、零或正整数,和排序问题怎么联系:
返回这3种值对我们来说或许没有意义,但是这3个值告诉底层如何判断2个对象的大小,至于排序,我们是通过Collections.sort和Arrays.sort进行,而这2个方法在底层实现时,
使用到了object1.compareTo(object2)这种方法进行判断谁大谁小,从而调整数组,最终给你返回有序的集合.注:你可以参考源代码,所有的排序都要用到调用的是
Arrays.mergeSort(Object[] src,Object[] dest,int low,int high,int off).
2.Comparable还有Comparator的使用:当你自己写类时,如果希望这个类能按照自己的意愿进行排序,你就实现Comparable接口,你就是只要告诉底层怎么判断大小
(即compareTo()),然后想排序,就是用Collections.sort(List list)即可;
而Comparator的使用:强行对某个对象 collection 进行整体排序,而集合里的对象可以是没有实现Comparable接口的对象,也可以是实现了Comparable接口的对象,
使用这个排序器可以改变默认的排序时使用比较大小的方法,这时候底层进行排序就不再是用默认的自然排序,在底层对集合进行排序将不再是用
Arrays.mergeSort(Object[] src,Object[] dest,int low,int high,int off),而是是用Arrays.mergeSort
(Object[] src,Object[] dest,int low,int high,int off,Comparator c),比较大小则是用了c.compare(dest[j-1], dest[j])。

三:
        

java中compareTo和compare方法之比较

这两个方法经常搞混淆,现对其进行总结以加深记忆。
  1. compareTo(Object o)方法是java.lang.Comparable<T>接口中的方法,当需要对某个类的对象进行排序时,该类需要实现Comparable<T>接口的,必须重写public int compareTo(T o)方法,比如MapReduce中Map函数和Reduce函数处理的 <key,value>,其中需要根据key对键值对进行排序,所以,key实现了WritableComparable<T>接口,实现这个接口可同时用于序列化和反序列化。WritableComparable<T>接口(用于序列化和反序列化)是Writable接口和Comparable<T>接口的组合;
  2. compare(Object o1,Object o2)方法是java.util.Comparator<T>接口的方法,它实际上用的是待比较对象的compareTo(Object o)方法。
下面我们写一来看看上面两个方法是怎么用的: 首先,写一个User类,代码如下:
public class User implements Comparable<Object>{
    int id;
    String name;
    
    public User(int id,String name){
        this.id = id;
        this.name = name;
    }
    /*
     * Getters and Setters
    */
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
       
    @Override
    public int compareTo(Object o) {
        if(this ==o){
            return 0;            
        }
        else if (o!=null && o instanceof User) {   
            User u = (User) o; 
            if(id<=u.id){
                return -1;
            }else{
            return 1;
        }
    }else{
        return -1;
    }
}
}
           
接下来,我们写一个测试类Test:
public class Test{
    //编写Comparator,根据User的id对User进行排序
    private static final Comparator<User> COMPARATOR = new Comparator<User>() {
       public int compare(User o1, User o2) {
           return o1.compareTo(o2);//运用User类的compareTo方法比较两个对象       
      }
   };
    
    public static void main(String[] args) {
        ArrayList<User> student = new ArrayList<User>();
        User user1 = new User(1,"yueliming");
        User user2 = new User(2,"yueliming");
    
        Collections.sort(student, COMPARATOR);//用我们写好的Comparator对student进行排序
        for(int i=0;i<student.size();i++){
            System.out.println(student.get(i).getId());
        }
    }
}
           
四: ompareTo(Object o)方法是java.lang.Comparable<T>接口中的方法,当需要对某个类的对象进行排序时,该类需要实现 Comparable<T>接口的, 必须重写public int compareTo(T o)方法。 compare(Object o1,Object o2)方法是java.util.Comparator<T>接口的方法,它实际上用的是待比较对象的compareTo(Object o)方法。 Comparable接口: java.lang.Comparable接口会强行对实现它的每一个类的对象进行整体排序,这种排序被称为类的自然排序。这个接口中声明了一个自然比较方法, 该方法的声明和描述形式如下:int compareTo(T o):比较此对象与指定对象的顺序。如果该对象小于指定对象,则返回一个负整数,如果该对象等于指定对象, 则返回一个零,如果该对象大于指定对象就返回一个正整数。 Comparator接口: 使用Comparable接口定义排序有着明显的局限性,一个类只能实现该接口一次,即只能定义一种自然排序的规则,如果在需要一个集合中按学生的考试得分对学生进行 排序,而在另一个集合中需要对学生姓名进行排序,那该怎么办? 在这种情况下可以让排序集合使用不同的排序方法,将一个实现Comparator接口的类的对象传递给排序集合的构造函数。 Comparator接口拥有一个方法,它的声明和描述如下: int compare(T o1, T o2):此方法用来比较排序的两个函数,根据第一个参数小于,等于,大于分别返回负整数,零,正整数。 参考:http://www.cnblogs.com/yueliming/archive/2013/05/22/3092576.html http://www.strutshome.com/index.php/archives/289 http://zhidao.baidu.com/question/66775343.html?qbl=relate_question_2&word=compare%20compareTo&optimi=4 http://blog.csdn.net/mageshuai/article/details/3849143

五:

Arrays.sort是数组的比较  可以用比较器comparator

static

<T> void

sort(T[] a,Comparator<? super T> c)

          根据指定比较器产生的顺序对指定对象数组进行排序。

static

<T> void

sort(T[] a, int fromIndex, int toIndex,Comparator<? super T> c)

          根据指定比较器产生的顺序对指定对象数组的指定范围进行排序。

Collections.sort是对集合list的比较  可以用比较器comparator

之所以是对list适用,因为set在加入的时候如果是TreeSet会自动根据自定义的排序规则排序,但是list在加入的时候是有序的,包括LinkedList都是根据插入的顺序来排序,所以此时需要用Collections.sort(list)来排序。

//        Collections.sort(list,new UserComparactor());

//        Collections.sort(list);

static

<T extends Comparable<? super T>> void

sort(List<T> list)

          根据元素的自然顺序 对指定列表按升序进行排序。

static

<T> void

sort(List<T> list,Comparator<? super T> c)

          根据指定比较器产生的顺序对指定列表进行排序。

compareto的比较机制      
compareTo()的返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的 

差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方 

全比较完,这时就比较字符的长度. 

例: 
String s1 = "abc"; 
String s2 = "abcd"; 
String s3 = "abcdfg"; 
String s4 = "1bcdfg"; 
String s5 = "cdfg"; 
System.out.println( s1.compareTo(s2) ); // -1 (前面相等,s1长度小1) 
System.out.println( s1.compareTo(s3) ); // -3 (前面相等,s1长度小3) 
System.out.println( s1.compareTo(s4) ); // 48 ("a"的ASCII码是97,"1"的的ASCII码是49,所以返回48) 
System.out.println( s1.compareTo(s5) ); // -2 ("a"的ASCII码是97,"c"的ASCII码是99,所以返回-2)
字典顺序就用compareTo ,String的比较实现是按照alphabetic(字典顺序)做的,Integer类实现了Comparable接口的compareTo()方法,所以直接可以使用

comparable代码举例:
       
package compare.itany.www;
public class User implements Comparable
{
    private int id;
    private String name;
    public void setId(int id)
    {
        this.id = id;
    }
    public void setName(String name)
    {
        this.name = name;
    }
    public int getId()
    {
        return id;
    }
    public String getName()
    {
        return name;
    }
    public User(int id,String name)
    {
        this.id=id;
        this.name=name;
    }
    public int compareTo(Object o)
    {
        if(o!=this)
        {
            User u=(User)o;
            boolean flag= name.equals(u.name);
            if(id<u.id)
            {
                return -1;//正常情况下<是升序排列 即自己的比别人的小放在前面 即升序是-1  此处自己的比别人的小为正 则是降序
            }
            else if(id==u.id)
            {
                return name.compareTo(u.name);
//                return 0;
            }
        }
        return 1;
    }
    public String toString()
    {
        return id+" "+name;
    }
}
           
package compare.itany.www;

import java.util.TreeSet;

public class Test
{
    
    public static void main(String[] args)
    {
        TreeSet<User> set=new TreeSet<User>();
        set.add(new User(2,"wang"));
        set.add(new User(1,"lang"));
        set.add(new User(4,"cang"));
        set.add(new User(2,"aang"));
        for(User u:set)
        {
            System.out.println(u);
        }
    }
}
           
先比较ID 相同时再比较字典顺序 输出: 1 lang 2 aang 2 wang 4 cang 如果两次比较都是一样的 那么对于set来说会被删除一个
comparator代码举例:

      
package compare.itany.www;

import java.util.Comparator;

public class UserComparactor implements Comparator
{
    public int compare(Object o1,Object o2)
    {
        User u1=(User)o1;
        User u2=(User)o2;
        u1.compareTo(u2);
        return 0;
    }
}
           
package compare.itany.www;

import java.util.TreeSet;

public class TestComparator
{
    
    public static void main(String[] args)
    {
        TreeSet<User> set=new TreeSet<User>();
        set.add(new User(3,"wang"));
        set.add(new User(1,"lang"));
        set.add(new User(4,"cang"));
        set.add(new User(3,"zang"));
        for(User u:set)
        {
            System.out.println(u);
        }
    }
}
           
输出:
先比较ID 相同时再比较字典顺序      
1 lang 3 wang 3 zang 4 cang