Java中Comparable和Comparator你知多少?

 前言:

 我喜歡這種遨遊在Java的世界裏,精心研究學習新鮮事物的感受,即使再小再細再微不足道的東西,也讓我樂此不疲,同時我也更願意將我所會的東西分享出來供你們學習以及方便本身往後回顧。好了,閒話很少說,今天主要是介紹Comparable和Comparator倆個接口(參照JavaAPI)以及差別,而且經過一些示例進行說明。html


 

Comparable

1.簡介:

此接口強行對實現它的每一個類的對象進行總體排序。這種排序被稱爲類的天然排序,類的 compareTo 方法被稱爲它的天然比較方法。java

實現此接口的對象列表(和數組)能夠經過 Collections.sort(和 Arrays.sort)進行自動排序。實現此接口的對象能夠用做有序映射(如TreeMap)中的鍵或有序集合(TreeSet)中的元素,無需指定比較器sql

對於類 C 的每個 e1 和 e2 來講,當且僅當 e1.compareTo(e2) == 0 與 e1.equals(e2) 具備相同的 boolean 值時,類 C 的天然排序才叫作與 equals 一致。注意,null 不是任何類的實例,即便 e.equals(null) 返回 falsee.compareTo(null) 也將拋出 NullPointerExceptionapi

建議(雖然不是必需的)最好使天然排序與 equals 一致。這是由於在使用天然排序與 equals 不一致的元素(或鍵)時,沒有顯式比較器的有序集合(和有序映射表)行爲表現「怪異」。尤爲是,這樣的有序集合(或有序映射表)違背了根據 equals 方法定義的集合(或映射表)的常規協定。數組

例如,若是將兩個鍵 a 和 b 添加到沒有使用顯式比較器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那麼第二個 add 操做將返回 false(有序集合的大小沒有增長),由於從有序集合的角度來看,a 和 b 是相等的。數據結構

實際上,全部實現 Comparable 的 Java 核心類都具備與 equals 一致的天然排序。java.math.BigDecimal 是個例外,它的天然排序將值相等但精確度不一樣的BigDecimal 對象(好比 4.0 和 4.00)視爲相等。app

從數學上講,定義給定類 C 上天然排序的關係式 以下:ide

      {(x, y)|x.compareTo(y) <= 0}。

總體排序的  是:函數

      {(x, y)|x.compareTo(y) == 0}。

它直接遵循 compareTo 的協定,商是 C 的 等價關係,天然排序是 C 的 總體排序。當說到類的天然排序 與 equals 一致 時,是指天然排序的商是由類的equals(Object) 方法定義的等價關係。學習

    {(x, y)|x.equals(y)}。

此接口是 Java Collections Framework 的成員。

2.全部已知實現該接口的類:

Authenticator.RequestorTypeBigDecimalBigIntegerBooleanByteByteBufferCalendarCharacterCharBufferCharset,ClientInfoStatusCollationKeyComponent.BaselineResizeBehaviorCompositeNameCompoundNameDateDateDesktop.Action,Diagnostic.KindDialog.ModalExclusionTypeDialog.ModalityTypeDoubleDoubleBufferDropModeElementKindElementTypeEnum,FileFloatFloatBufferFormatter.BigDecimalLayoutFormFormSubmitEvent.MethodTypeGregorianCalendarGroupLayout.Alignment,IntBufferIntegerJavaFileObject.KindJTable.PrintModeKeyRep.TypeLayoutStyle.ComponentPlacementLdapNameLongLongBuffer,MappedByteBufferMemoryTypeMessageContext.ScopeModifierMultipleGradientPaint.ColorSpaceType,MultipleGradientPaint.CycleMethodNestingKindNormalizer.FormObjectNameObjectStreamFieldProxy.TypeRdn,Resource.AuthenticationTypeRetentionPolicyRoundingModeRowFilter.ComparisonTypeRowIdLifetimeRowSorterEvent.Type,Service.ModeShortShortBufferSOAPBinding.ParameterStyleSOAPBinding.StyleSOAPBinding.UseSortOrderSourceVersion,SSLEngineResult.HandshakeStatusSSLEngineResult.StatusStandardLocationStringSwingWorker.StateValueThread.StateTime,TimestampTimeUnitTrayIcon.MessageTypeTypeKindURIUUIDWebParam.ModeXmlAccessOrderXmlAccessTypeXmlNsForm

3.全部已知子接口:

DelayedNameRunnableScheduledFuture<V>, ScheduledFuture<V>

4.接口中方法介紹:

該接口只有一個方法:

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}

說明:
假設咱們經過 x.compareTo(y) 來「比較x和y的大小」。若返回「負數」,意味着「x比y小」;返回「零」,意味着「x等於y」;返回「正數」,意味着「x大於y」。 


 

 

Comparator

1.簡介:

強行對某個對象 collection 進行總體排序的比較函數。能夠將 Comparator 傳遞給 sort 方法(如 Collections.sort 或 Arrays.sort),從而容許在排序順序上實現精確控制。還可使用 Comparator 來控制某些數據結構(如有序 set有序映射)的順序,或者爲那些沒有天然順序的對象 collection 提供排序。

當且僅當對於一組元素 S 中的每一個 e1 和 e2 而言,c.compare(e1, e2)==0 與 e1.equals(e2) 具備相等的布爾值時,Comparator c 強行對 S 進行的排序才叫作與 equals 一致 的排序。

當使用具備與 equals 不一致的強行排序能力的 Comparator 對有序 set(或有序映射)進行排序時,應該當心謹慎。假定一個帶顯式 Comparator c 的有序 set(或有序映射)與從 set S 中抽取出來的元素(或鍵)一塊兒使用。若是 c 強行對 S 進行的排序是與 equals 不一致的,那麼有序 set(或有序映射)將是行爲「怪異的」。尤爲是有序 set(或有序映射)將違背根據 equals 所定義的 set(或映射)的常規協定。

例如,假定使用 Comparator c 將知足 (a.equals(b) && c.compare(a, b) != 0) 的兩個元素 a 和 b 添加到一個空 TreeSet 中,則第二個 add 操做將返回 true(樹 set 的大小將會增長),由於從樹 set 的角度來看,a 和 b 是不相等的,即便這與 Set.add 方法的規範相反。

注:一般來講,讓 Comparator 也實現 java.io.Serializable 是一個好主意,由於它們在可序列化的數據結構(像 TreeSetTreeMap)中可用做排序方法。爲了成功地序列化數據結構,Comparator(若是已提供)必須實現 Serializable

在算術上,定義給定 Comparator c 對給定對象 set S 實施強行排序 的關係式 爲:

       {(x, y) such that c.compare(x, y) <= 0}.

此總體排序的 商 (quotient) 爲:

       {(x, y) such that c.compare(x, y) == 0}.
 

它直接遵循 compare 的協定,商是 S 上的 等價關係,強行排序是 S 上的 總體排序。當咱們說 c 強行對 S 的排序是 與 equals 一致 的時,意思是說排序的商是對象的 equals(Object) 方法所定義的等價關係:

       {(x, y) such that x.equals(y)}. 

此接口是 Java Collections Framework 的成員。

 

 

2.接口中方法介紹:

該接口有倆個方法:

package java.util;

public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);
}

說明:
(1) 若一個類要實現Comparator接口:它必定要實現compareTo(T o1, T o2) 函數,但能夠不實現 equals(Object obj) 函數。

         由於任何類,默認都是已經實現了equals(Object obj)的。 Java中的一切類都是繼承於java.lang.Object,在Object.java中實現了equals(Object obj)函數;因此,其它全部的類也至關於都實現了該函數。

(2) int compare(T o1, T o2) 是「比較o1和o2的大小」。返回「負數」,意味着「o1比o2小」;返回「零」,意味着「o1等於o2」;返回「正數」,意味着「o1大於o2」。


 

 

Comparator 和 Comparable 比較

Comparable是排序接口;若一個類實現了Comparable接口,就意味着「該類支持排序」。
而Comparator是比較器;咱們若須要控制某個類的次序,能夠創建一個「該類的比較器」來進行排序。

咱們不難發現:Comparable至關於「內部比較器」,而Comparator至關於「外部比較器」。


 

 

最後經過測試程序來對倆個接口進行說明:

Comparable接口測試代碼:

public class Book implements Comparable { // 定義名爲Book的類,默認繼承自Object類  
    public int id;// 編號  
    public String name;// 名稱  
    public double price; // 價格  
    private String author;// 做者  
    public GregorianCalendar calendar;// 出版日期  
  
    public Book() {  
        this(0, "X", 0.0, new GregorianCalendar(), "");  
    }  
  
    public Book(int id, String name, double price, GregorianCalendar calender,  
            String author) {  
        this.id = id;  
        this.name = name;  
        this.price = price;  
        this.calendar = calender;  
        this.author = author;  
    }  
  
    // 重寫繼承自父類Object的方法,知足Book類信息描述的要求  
    public String toString() {  
        String showStr = id + "\t" + name; // 定義顯示類信息的字符串  
        DecimalFormat formatPrice = new DecimalFormat("0.00");// 格式化價格到小數點後兩位  
        showStr += "\t" + formatPrice.format(price);// 格式化價格  
        showStr += "\t" + author;  
        SimpleDateFormat formatDate = new SimpleDateFormat("yyyy年MM月dd日");  
        showStr += "\t" + formatDate.format(calendar.getTime()); // 格式化時間  
        return showStr; // 返回類信息字符串  
    }  
  
    public int compareTo(Object obj) {// Comparable接口中的方法  
        Book b = (Book) obj;  
        return (int) (this.price - b.price); // 按書的id比較大小,用於默認排序  
    }  
  
    public static void main(String[] args) {  
        Book b1 = new Book(1, "紅樓夢", 130.86, new GregorianCalendar(2009,  
                01, 25), "曹雪芹、高鄂");  
        Book b2 = new Book(2, "三國演義", 99.99, new GregorianCalendar(2008, 7,  
                8), "羅貫中 ");  
        Book b3 = new Book(3, "水滸傳", 80.3, new GregorianCalendar(2009, 6,  
                28), "施耐庵 ");  
        Book b4 = new Book(4, "西遊記", 111.8, new GregorianCalendar(2011, 6,  
                8), "吳承恩");  
        Book b5 = new Book(5, "天龍八部", 8.4, new GregorianCalendar(2011, 9,  
                23), "金庸");  
        TreeMap<Object,Integer> tm = new TreeMap<Object, Integer>();  
        tm.put(b1, new Integer(2550));  
        tm.put(b2, new Integer(1220));  
        tm.put(b3, new Integer(324));  
        tm.put(b4, new Integer(453));  
        tm.put(b5, new Integer(40)); 
        Iterator it = tm.keySet().iterator();
        Object key = null, value = null;  
        Book bb = null;  
        while (it.hasNext()) {  
         key = it.next();  
         bb = (Book) key;  
         value = tm.get(key);  
         System.out.println(bb.toString() + "\t庫存:" + tm.get(key));  
     } 
        
    }  
    
}  

代碼講解:

建立了一個book實體類,繼承了Comparable,此時它將能夠進行排序。

public class Book implements Comparable {
.
.
.

}

 建立一個帶參構造器,用於排序使用。

 public Book(int id, String name, double price, GregorianCalendar calender,  
            String author) {  
        this.id = id;  
        this.name = name;  
        this.price = price;  
        this.calendar = calender;  
        this.author = author;  
    }  

重寫繼承自父類Object的方法,知足Book類信息描述的要求 .

public String toString() {  
        String showStr = id + "\t" + name; // 定義顯示類信息的字符串  
        DecimalFormat formatPrice = new DecimalFormat("0.00");// 格式化價格到小數點後兩位  
        showStr += "\t" + formatPrice.format(price);// 格式化價格  
        showStr += "\t" + author;  
        SimpleDateFormat formatDate = new SimpleDateFormat("yyyy年MM月dd日");  
        showStr += "\t" + formatDate.format(calendar.getTime()); // 格式化時間  
        return showStr; // 返回類信息字符串  
    }  

Comparable接口中的方法,按書的價格asc排序(排序呢方式能夠自定義)

 public int compareTo(Object obj) {// Comparable接口中的方法  
        Book b = (Book) obj;  
        return (int) (this.price - b.price); // 按書的價格比較大小,用於默認排序  
    }  

在main函數中使用treeMap對其默認排序。

 Book b1 = new Book(10000, "紅樓夢", 150.86, new GregorianCalendar(2009,  
                01, 25), "曹雪芹、高鄂");  
        Book b2 = new Book(10001, "三國演義", 99.68, new GregorianCalendar(2008, 7,  
                8), "羅貫中 ");  
        Book b3 = new Book(10002, "水滸傳", 100.8, new GregorianCalendar(2009, 6,  
                28), "施耐庵 ");  
        Book b4 = new Book(10003, "西遊記", 120.8, new GregorianCalendar(2011, 6,  
                8), "吳承恩");  
        Book b5 = new Book(10004, "天龍八部", 10.4, new GregorianCalendar(2011, 9,  
                23), "搜狐");  
        TreeMap<Object,Integer> tm = new TreeMap<Object, Integer>();  
        tm.put(b1, new Integer(255));  
        tm.put(b2, new Integer(122));  
        tm.put(b3, new Integer(688));  
        tm.put(b4, new Integer(453));  
        tm.put(b5, new Integer(40)); 
        Iterator it = tm.keySet().iterator();
        Object key = null, value = null;  
        Book bb = null;  
     while (it.hasNext()) {  
         key = it.next();  
         bb = (Book) key;  
         value = tm.get(key);  
         System.out.println(bb.toString() + "\t庫存:" + tm.get(key));  
     } 

最終運行獲得結果爲:

10004	天龍八部	10.40	搜狐	2011年10月23日	庫存:40
10001	三國演義	99.68	羅貫中 	2008年08月08日	庫存:122
10002	水滸傳	100.80	施耐庵 	2009年07月28日	庫存:688
10003	西遊記	120.80	吳承恩	2011年07月08日	庫存:453
10000	紅樓夢	150.86	曹雪芹、高鄂	2009年02月25日	庫存:255

Comparator測試代碼:

/**
 * 
 * UseComparator      
 * @author LongJin
 * 2017年2月28日 下午2:26:45   
 */
public class UseComparator {
	public static void main(String args[]) {  
        List<Book> list = new ArrayList<Book>(); // 數組序列  
        Book b1 = new Book(10000, "紅樓夢", 150.86, new GregorianCalendar(2009,  
                01, 25), "曹雪芹、高鄂");  
        Book b2 = new Book(10001, "三國演義", 99.68, new GregorianCalendar(2008, 7,  
                8), "羅貫中 ");  
        Book b3 = new Book(10002, "水滸傳", 100.8, new GregorianCalendar(2009, 6,  
                28), "施耐庵 ");  
        Book b4 = new Book(10003, "西遊記", 120.8, new GregorianCalendar(2011, 6,  
                8), "吳承恩");  
        Book b5 = new Book(10004, "天龍八部", 10.4, new GregorianCalendar(2011, 9,  
                23), "搜狐");  
        list.add(b1);  
        list.add(b2);  
        list.add(b3);  
        list.add(b4);  
        list.add(b5);  
        // Collections.sort(list); //沒有默認比較器,不能排序  
        System.out.println("數組序列中的元素:");  
        myprint(list);  
        Collections.sort(list, new PriceComparator()); // 根據價格排序  
        System.out.println("按書的價格排序:");  
        myprint(list);  
        Collections.sort(list, new CalendarComparator()); // 根據時間排序  
        System.out.println("按書的出版時間排序:");  
        myprint(list);  
    }  
  
    // 自定義方法:分行打印輸出list中的元素  
    public static void myprint(List<Book> list) {  
        Iterator it = list.iterator(); // 獲得迭代器,用於遍歷list中的全部元素  
        while (it.hasNext()) {// 若是迭代器中有元素,則返回true  
            System.out.println("\t" + it.next());// 顯示該元素  
        }  
    }  
  
    // 自定義比較器:按書的價格排序  升序 
    static class PriceComparator implements Comparator<Book> {
		@Override
		public int compare(Book o1, Book o2) {
			// TODO Auto-generated method stub
			return new Double(o1.price).compareTo(new Double(o2.price));  
		}
    	
    }  
  
    // 自定義比較器:按書出版時間來排序  降序
    static class CalendarComparator implements Comparator<Book> {  
		@Override
		public int compare(Book o1, Book o2) {
			// TODO Auto-generated method stub
			return o2.calendar.compareTo(o1.calendar);
		}  
    }  
}

自定義比較器按書的價格升序:

  // 自定義比較器:按書的價格排序  
    static class PriceComparator implements Comparator<Book> {
		@Override
		public int compare(Book o1, Book o2) {
			// TODO Auto-generated method stub
			return new Double(o1.price).compareTo(new Double(o2.price));  
		}
    	
    }  

自定義比較器按出版時間降序:

// 自定義比較器:按書出版時間來排序  
    static class CalendarComparator implements Comparator<Book> {  
		@Override
		public int compare(Book o1, Book o2) {
			// TODO Auto-generated method stub
			return o2.calendar.compareTo(o1.calendar);
		}  
    }  

在main中添加book的list數組:

List<Book> list = new ArrayList<Book>(); // 數組序列  
        Book b1 = new Book(10000, "紅樓夢", 150.86, new GregorianCalendar(2009,  
                01, 25), "曹雪芹、高鄂");  
        Book b2 = new Book(10001, "三國演義", 99.68, new GregorianCalendar(2008, 7,  
                8), "羅貫中 ");  
        Book b3 = new Book(10002, "水滸傳", 100.8, new GregorianCalendar(2009, 6,  
                28), "施耐庵 ");  
        Book b4 = new Book(10003, "西遊記", 120.8, new GregorianCalendar(2011, 6,  
                8), "吳承恩");  
        Book b5 = new Book(10004, "天龍八部", 10.4, new GregorianCalendar(2011, 9,  
                23), "搜狐"); 
     // 添加對象到ArrayList中
        list.add(b1);  
        list.add(b2);  
        list.add(b3);  
        list.add(b4);  
        list.add(b5); 

自定義方法:分行打印輸出list中的元素  :

  public static void myprint(List<Book> list) {  
        Iterator it = list.iterator(); // 獲得迭代器,用於遍歷list中的全部元素  
        while (it.hasNext()) {// 若是迭代器中有元素,則返回true  
            System.out.println("\t" + it.next());// 顯示該元素  
        }  
    }  

最後將list數組以不一樣的比較器進行排序:

//Collections.sort(list); //沒有默認比較器,不能排序  但這裏的BOok類實現了Comparable接口,因此會以默認比較器排序
        System.out.println("數組序列中的元素:");  
        myprint(list);  
        Collections.sort(list, new PriceComparator()); // 根據價格排序  
        System.out.println("按書的價格排序:");  
        myprint(list);  
        Collections.sort(list, new CalendarComparator()); // 根據時間排序  
        System.out.println("按書的出版時間排序:");  
        myprint(list);  

運行代碼,得出結果:

數組序列中的元素:
	10000	紅樓夢	150.86	曹雪芹、高鄂	2009年02月25日
	10001	三國演義	99.68	羅貫中 	2008年08月08日
	10002	水滸傳	100.80	施耐庵 	2009年07月28日
	10003	西遊記	120.80	吳承恩	2011年07月08日
	10004	天龍八部	10.40	搜狐	2011年10月23日
按書的價格排序:
	10004	天龍八部	10.40	搜狐	2011年10月23日
	10001	三國演義	99.68	羅貫中 	2008年08月08日
	10002	水滸傳	100.80	施耐庵 	2009年07月28日
	10003	西遊記	120.80	吳承恩	2011年07月08日
	10000	紅樓夢	150.86	曹雪芹、高鄂	2009年02月25日
按書的出版時間排序:
	10004	天龍八部	10.40	搜狐	2011年10月23日
	10003	西遊記	120.80	吳承恩	2011年07月08日
	10002	水滸傳	100.80	施耐庵 	2009年07月28日
	10000	紅樓夢	150.86	曹雪芹、高鄂	2009年02月25日
	10001	三國演義	99.68	羅貫中 	2008年08月08日
相關文章
相關標籤/搜索