天天看點

深入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