5 分鐘搞定 Java Comparable 介面

dav2100發表於2021-09-09

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

這篇文章是免費Java 8 課程中關於Clean Code原則的一部分。

在這篇文章中,我們要聊一聊Java Comparable介面。

還有一個不錯的視訊可以點這裡

Comparable介面時用來幹什麼的呢?

我們應該如何對事物進行比較和排序?這問題聽上去有點莫名其妙,但我希望你認真考慮一下。比方說,我們有一組蘋果:

例1

我們要怎樣對它們進行排序呢?我們希望通過重量進行排序嗎?如果是的話,排序是從最輕到最重還是從最重到最輕?當我們對它們進行排序的時候,我們需要反覆比較兩個蘋果的重量,直到正確地排好所有的蘋果。蘋果1比蘋果2重?那它比蘋果3重嗎?我們需要不斷比較,直到完成排序。Comparable介面可以幫助我們實現這一目標。Comparable本身不能對物件進行排序,但介面定義的方法 int compareTo(T)可以。

compareTo(T)如何工作

讓我們通過使用compareTo()方法來看看哪個蘋果更重,開始吧。

例2

compareTo()方法的工作原理是返回一個int值——或正,或負,或為零。它通過呼叫作為引數的物件來比較物件。負數表示呼叫的物件比引數“輕”。如果我們用大小來比較蘋果,那麼上面的呼叫會返回一個負數,例如-400,因為紅蘋果比青蘋果小。如果兩個蘋果重量相等,那麼呼叫將返回0。如果紅蘋果更重,那麼compareTo()將返回一個正數,例如68。

compareTo()的靈活性

如果我們反覆呼叫上面的compareTo()方法,那麼我們可以通過大小來排序,這很棒,但並非故事的結束。如果我們想通過顏色來給蘋果排序呢?抑或是重量?我們也可以做到。關鍵是,我們的客戶——讓我們叫他胖子農夫(見例3),需要在我們開始開發之前精確定義需要如何對蘋果進行排序。

例3

他可以通過回答這兩個問題來做到這一點:

  1. 他希望蘋果如何進行排序?他希望我們比較什麼特徵?
  2. 在那樣的環境中,“小於”,“等於”和“大於”指的是什麼意思?

也可以使用多個特徵,這個後面我們會講。

例1:通過重量排序蘋果

在第一個例子中,我們將通過重量對蘋果排序。只需要一行程式碼。

Collections.sort(apples);

例4

上面的程式碼行可以為我們做到所有的排序工作,只要我們事先定義好如何對蘋果進行排序(這就需要多行程式碼了)。

讓我們開始寫蘋果類吧。

public class Apple implements Comparable {
    private String variety;
    private Color color;
    private int weight;
    @Override
    public int compareTo(Apple other) {
        if (this.weight < other.weight) {
            return -1;
        }
        if (this.weight == other.weight) {
            return 0;
        }
        return 1;
    }
}

例5

這是Apple類的第一個版本。由於我們使用的是compareTo方法,並且正在排序蘋果,所以我實現了Comparable介面。在這第一個版本中,我們通過重量比較物件。在我們的compareTo()方法中,我們寫一個if條件,說明如果這個蘋果的重量小於其他的蘋果,那麼返回一個負數,為了保持簡單,我們假定它為-1。請記住,這意味著這個蘋果輕於Apple ‘other’。在第二個if語句中,我們要說明,如果蘋果重量相等,那麼返回一個0。當然,如果這個蘋果既不是更輕,又不是一樣重,那就只能比其他蘋果更重了。在這種情況下,我們返回一個正數,假定為1。

例2:通過多個特徵排序蘋果

正如我前面提到的,我們還可以使用compareTo()比較多個特徵。比方說,我們第一通過品種排序蘋果,但如果兩個蘋果是同一品種,那麼我們就按顏色排序。最後,如果這兩個特性相同,那麼我們將按重量排序。雖然我們可以手動實現這件事,就像我在最後一個例子中做的那樣,但是其實可以用一種簡潔得多的方式實現。一般來說,最好是重用現有的程式碼,而不是自己寫。我們可以在Integer、String和列舉類中使用compareTo方法來比較值。由於我們沒有使用Integer物件,用了int,所以我們不得不使用來自於Integer包裝器類的一個靜態的helper方法來比較兩個值。

public class Apple implements Comparable {
    private String variety;
    private Color color;
    private int weight;
    @Override
    public int compareTo(Apple other) {
        int result = this.variety.compareTo(other.variety);
        if (result != 0) {
            return result;
        }
        if (result == 0) {
            result = this.color.compareTo(other.color);
        }
        if (result != 0) {
            return result;
        }
        if (result == 0) {
            result = Integer.compare(this.weight, other.weight);
        }
        return result;
    }
}

例6

在例6中,我們比較了客戶指定的蘋果的第一特性,它們的品種。如果compareTo()呼叫的結果為非零,那麼我們返回值。否則,我們呼叫另一個compareTo()直到得到一個非零值,或者直到已經比較完這三個特徵。儘管此程式碼可以工作,但它不是最有效或乾淨的解決方案。在例3中,我們重構我們的程式碼,使其更簡單。

@Override
public int compareTo(Apple other) {
     int result = this.variety.compareTo(other.variety);
     if (result == 0) {
          result = this.color.compareTo(other.color);
     }
     if (result == 0) {
          result = Integer.compare(this.weight, other.weight);
     }
     return result;
}

例7

正如你所看到的,這大大減少了程式碼,並且每一次比較只要一行程式碼。如果一個compareTo()呼叫的結果是零,那麼我們就轉移到下一個相同if語句的比較中。順便說一句,這是成為Clean Coder的一個很好的例子。通常情況下,你不需要立即寫出乾淨的程式碼;你可以從一個粗略的想法開始,使其可以工作,然後不斷改進,直到你儘可能得讓它乾淨就可以了。

Comparable,hashCode以及Equals

你可能會注意到compareTo()看起來有點像hashCode()和equals()方法。但是,它們有一個重要的區別。對於hashCode()和equals()方法,比較個體屬性的順序不影響返回的值,但是,在compareTo()中,通過你比較物件的順序來定義物件的順序。

結論

在結論中我只想強調Comparable介面是多麼的重要。它既用於java.util.Arrays,也用於java.util.Collections實用程式類,來排序元素和搜尋排序集合中的元素。使用TreeSet和Tree Map,就更簡單了——想要它們會自動排序必須實現Comparable介面的元素。

譯文連結:http://www.codeceo.com/article/java-comparable-5-minutes.html
英文原文:Java Comparable Interface in Five Minutes
翻譯作者:碼農網 – 小峰
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章