use*_*315 22 c# java comparable comparator
我最近在SO聊天中看到了一个讨论,但没有明确的结论,所以我最后在那里问.
这是出于历史原因还是与其他语言的一致性?在查看compareTo各种语言的签名时,它会返回一个int.
为什么它不返回枚举.例如在C#中我们可以这样做:
enum CompareResult {LessThan, Equals, GreaterThan};
Run Code Online (Sandbox Code Playgroud)
并且:
public CompareResult CompareTo(Employee other) {
if (this.Salary < other.Salary) {
return CompareResult.LessThan;
}
if (this.Salary == other.Salary){
return CompareResult.Equals;
}
return CompareResult.GreaterThan;
}
Run Code Online (Sandbox Code Playgroud)
在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() {}
}
Run Code Online (Sandbox Code Playgroud)
和
interface Comparable<T> {
Compare compareTo(T obj);
}
Run Code Online (Sandbox Code Playgroud)
我问这个是因为我不认为一个int代表数据的语义.
例如在C#中,
l.Sort(delegate(int x, int y)
{
return Math.Min(x, y);
});
Run Code Online (Sandbox Code Playgroud)
在Java 8中它的双胞胎,
l.sort(Integer::min);
Run Code Online (Sandbox Code Playgroud)
编译两者都因为Min/min尊重比较器接口的契约(取两个int并返回一个int).
显然,两种情况下的结果都不是预期的结果.如果返回类型是Compare因为它会导致编译错误,从而迫使您实现"正确"行为(或者至少您知道自己在做什么).
这种返回类型会丢失很多语义(并且可能会导致一些难以找到的错误),那么为什么要这样设计呢?
Mat*_*son 21
[这个答案适用于C#,但它在某种程度上也可能适用于Java.]
这是出于历史,性能和可读性的原因.它可能会在两个地方提高性能:
<=和>=自然地表示相应的比较.与使用枚举相比,这将使用单个IL(因此处理器)指令(尽管有一种方法可以避免枚举的开销,如下所述).例如,我们可以检查lhs值是否小于或等于rhs值,如下所示:
if (lhs.CompareTo(rhs) <= 0)
...
Run Code Online (Sandbox Code Playgroud)
使用枚举,看起来像这样:
if (lhs.CompareTo(rhs) == CompareResult.LessThan ||
lhs.CompareTo(rhs) == CompareResult.Equals)
...
Run Code Online (Sandbox Code Playgroud)
这显然不太可读,而且效率也很低,因为它进行了两次比较.您可以通过使用临时结果来解决效率低下问题:
var compareResult = lhs.CompareTo(rhs);
if (compareResult == CompareResult.LessThan || compareResult == CompareResult.Equals)
...
Run Code Online (Sandbox Code Playgroud)
它仍然是一个不太可读的IMO - 它仍然效率较低,因为它做了两个比较操作而不是一个(尽管我自由地承认这种性能差异很可能很少).
正如raznagul在下面指出的那样,你只需要进行一次比较即可实现:
if (lhs.CompareTo(rhs) != CompareResult.GreaterThan)
...
Run Code Online (Sandbox Code Playgroud)
所以你可以使它相当有效 - 但当然,可读性仍然受到影响.... != GreaterThan不是那么清楚... <=
(如果你使用枚举,你当然无法避免将比较结果转换为枚举值的开销.)
因此,这主要是出于可读性的原因,但在某种程度上也是出于效率的原因.
最后,正如其他人所提到的,这也是出于历史原因.功能像C的strcmp()和memcmp()始终返回整数.
汇编程序比较指令也倾向于以类似的方式使用.
例如,要比较x86汇编程序中的两个整数,可以执行以下操作:
CMP AX, BX ;
JLE lessThanOrEqual ; jump to lessThanOrEqual if AX <= BX
Run Code Online (Sandbox Code Playgroud)
要么
CMP AX, BX
JG greaterThan ; jump to greaterThan if AX > BX
Run Code Online (Sandbox Code Playgroud)
要么
CMP AX, BX
JE equal ; jump to equal if AX == BX
Run Code Online (Sandbox Code Playgroud)
您可以看到与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!
Run Code Online (Sandbox Code Playgroud)
显然这是因为算术溢出了.如果你checked打开了构建,上面的代码实际上会抛出异常.
因此,最好避免优化使用减法来实现比较.(见下文Eric Lippert的评论.)
让我们坚持一个简单的事实,绝对最少的手工和/或不必要/不相关/实现相关的细节.
正如你已经想到的那样,compareTo它和Java一样古老(Since: JDK1.0来自Integer JavaDoc); 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可以解释as unknown/invalid value或out of bounds int值(例如,为仅正质量提供的负数)可以解释为错误代码.也许它不是最好的编码实践,但它肯定有它的优点,并且仍然常用于例如C.
另一方面,问"为什么XYZ语言的标准库确实符合ABC语言的遗留标准"本身没有实际意义,因为它只能通过设计实现它的语言来准确回答.
TL; DR就是这样,主要是因为遗留原因以及C语言程序员的POLA以遗留版本的方式完成,并且再次以向后兼容性和POLA保持这种方式.
作为旁注,我认为这个问题(目前的形式)过于宽泛,无法准确回答,高度基于意见,以及由于直接询问设计模式和语言架构而在SO上的偏离主题.
| 归档时间: |
|
| 查看次数: |
2460 次 |
| 最近记录: |