[英]Why does compareTo return an integer
我最近在SO聊天中看到了一個討論,但沒有明確的結論,所以我最后在那里問。
這是出於歷史原因還是與其他語言的一致性? 在查看各種語言的compareTo
的簽名時,它返回一個int
。
為什么它不返回枚舉。 例如在C#中我們可以這樣做:
enum CompareResult {LessThan, Equals, GreaterThan};
並且:
public CompareResult CompareTo(Employee other) {
if (this.Salary < other.Salary) {
return CompareResult.LessThan;
}
if (this.Salary == other.Salary){
return CompareResult.Equals;
}
return CompareResult.GreaterThan;
}
在Java中,枚舉是在這個概念之后引入的(我不記得有關C#)但它可以通過額外的類來解決,例如:
public final class CompareResult {
public static final CompareResult LESS_THAN = new Compare();
public static final CompareResult EQUALS = new Compare();
public static final CompareResult GREATER_THAN = new Compare();
private CompareResult() {}
}
和
interface Comparable<T> {
Compare compareTo(T obj);
}
我問這個是因為我不認為int
表示數據的語義。
例如在C#中,
l.Sort(delegate(int x, int y)
{
return Math.Min(x, y);
});
在Java 8中它的雙胞胎,
l.sort(Integer::min);
編譯兩者因為Min/min
尊重比較器接口的合約(取兩個int並返回一個int)。
顯然,兩種情況下的結果都不是預期的結果。 如果返回類型為Compare
,則會導致編譯錯誤,從而迫使您實現“正確”行為(或者至少您知道自己在做什么)。
這種返回類型會丟失很多語義(並且可能會導致一些難以找到的錯誤),那么為什么要這樣設計呢?
[這個答案適用於C#,但它在某種程度上也可能適用於Java。]
這是出於歷史,性能和可讀性的原因。 它可能會在兩個地方提高性能:
<=
和>=
自然地表示相應的比較。 與使用枚舉相比,這將使用單個IL(因此處理器)指令(盡管有一種方法可以避免枚舉的開銷,如下所述)。 例如,我們可以檢查lhs值是否小於或等於rhs值,如下所示:
if (lhs.CompareTo(rhs) <= 0)
...
使用枚舉,看起來像這樣:
if (lhs.CompareTo(rhs) == CompareResult.LessThan ||
lhs.CompareTo(rhs) == CompareResult.Equals)
...
這顯然不太可讀,而且效率也很低,因為它進行了兩次比較。 您可以通過使用臨時結果來解決效率低下問題:
var compareResult = lhs.CompareTo(rhs);
if (compareResult == CompareResult.LessThan || compareResult == CompareResult.Equals)
...
它仍然是一個不太可讀的IMO - 它仍然效率較低,因為它做了兩個比較操作而不是一個(盡管我自由地承認這種性能差異很可能很少)。
正如raznagul在下面指出的那樣,你只需要進行一次比較即可實現:
if (lhs.CompareTo(rhs) != CompareResult.GreaterThan)
...
所以你可以使它相當有效 - 但當然,可讀性仍然受到影響。 ... != GreaterThan
不如... <=
(如果你使用枚舉,你當然無法避免將比較結果轉換為枚舉值的開銷。)
因此,這主要是出於可讀性的原因,但在某種程度上也是出於效率的原因。
最后,正如其他人所提到的,這也是出於歷史原因。 像C的strcmp()
和memcmp()
這樣的函數總是返回整數。
匯編程序比較指令也傾向於以類似的方式使用。
例如,要比較x86匯編程序中的兩個整數,可以執行以下操作:
CMP AX, BX ;
JLE lessThanOrEqual ; jump to lessThanOrEqual if AX <= BX
要么
CMP AX, BX
JG greaterThan ; jump to greaterThan if AX > BX
要么
CMP AX, BX
JE equal ; jump to equal if AX == BX
您可以看到與CompareTo()返回值的明顯比較。
附錄:
這是一個例子,它表明使用從lhs中減去rhs來獲得比較結果的技巧並不總是安全的:
int lhs = int.MaxValue - 10;
int rhs = int.MinValue + 10;
// Since lhs > rhs, we expect (lhs-rhs) to be +ve, but:
Console.WriteLine(lhs - rhs); // Prints -21: WRONG!
顯然這是因為算術溢出了。 如果您為構建打開了checked
,則上面的代碼實際上會拋出異常。
因此,最好避免優化使用減法來實現比較。 (見下文Eric Lippert的評論。)
讓我們堅持一個簡單的事實,絕對最少的手工和/或不必要/不相關/實現相關的細節。
正如你已經想到的那樣, compareTo
和Java一樣古老了( Since: JDK1.0
來自Integer JavaDoc的 Since: JDK1.0
); Java 1.0被設計為C / C ++開發人員熟悉,並且模仿了它的許多設計選擇,無論好壞。 此外,Java具有向后兼容性策略 - 因此,一旦在核心庫中實現,該方法幾乎必將永遠保留在其中。
至於C / C ++ - strcmp
/ memcmp
,它與string.h一樣存在,所以基本上只要C標准庫,返回完全相同的值(或者更確切地說, compareTo
返回與strcmp
/ memcmp
相同的值) - 請參閱例如C ref - strcmp 。 在Java開始時,這種方式是合乎邏輯的事情。 當時Java中沒有任何枚舉,沒有泛型等等(所有這些都是> = 1.5)
strcmp
返回值的決定是非常明顯的 - 首先,你可以得到3個基本結果,因此為“更大”選擇+1,為“更小”選擇-1,為“相等”選擇0是合乎邏輯的事情去做。 此外,正如所指出的,您可以通過減法輕松獲得值,並且返回int
允許在進一步計算中使用它(以傳統的C類型不安全的方式),同時還允許有效的單操作實現。
如果您需要/想要使用基於enum
的類型安全比較接口 - 您可以自由地這樣做,但由於strcmp
返回+1
/ 0
/ -1
的約定與當代編程一樣古老,它實際上確實傳達了語義含義,以同樣的方式, null
可以被解釋為unknown/invalid value
或超出邊界的int值(例如,為僅正質量提供的負數)可以被解釋為錯誤代碼。 也許它不是最好的編碼實踐,但它肯定有它的優點,並且仍然常用於例如C.
另一方面,問“為什么XYZ語言的標准庫確實符合ABC語言的遺留標准”本身沒有實際意義,因為它只能通過設計實現它的語言來准確回答。
TL; DR就是這樣,主要是因為遺留原因以及C語言程序員的POLA以遺留版本的方式完成,並且再次以向后兼容性和POLA保持這種方式。
作為旁注,我認為這個問題(目前的形式)過於寬泛,無法准確回答,高度基於意見,以及由於直接詢問設計模式和語言架構而在SO上的偏離主題。
這種做法來自於以這種方式比較整數,並使用字符串的第一個非匹配字符之間的減法。
請注意,這種做法對於部分可比較的事物是危險的,而使用-1表示一對事物是無法比擬的。 這是因為它可能會創建<b和b <a(應用程序可能用來定義“無法比較”)的情況。 這種情況可能導致循環無法正確終止。
值為{lt,eq,gt,incomparable}的枚舉會更正確。
回復這是由於性能原因。 如果你需要經常比較int ,你可以返回以下內容:
實際比較通常作為減法返回。
舉個例子
public class MyComparable implements Comparable<MyComparable> {
public int num;
public int compareTo(MyComparable x) {
return num - x.num;
}
}
我的理解是這樣做是因為你可以對結果進行排序(即,操作是反身的和可傳遞的)。 例如,如果您有三個對象(A,B,C),則可以比較A-> B和B-> C,並使用結果值對它們進行正確排序。 有一個隱含的假設,如果A.compareTo(B)== A.compareTo(C)則B == C.
請參閱java的比較器文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.