Java 为什么JDK中的一些可比较类将比较函数限制为{−;1,0,1}和一些don';T

Java 为什么JDK中的一些可比较类将比较函数限制为{−;1,0,1}和一些don';T,java,comparable,Java,Comparable,当ta和tb相等时,java.lang.Compariable接口明确要求的唯一返回值是0。如果a小于b,则比较(a,b)必须为负数,不一定为负数−1和比较(b,a)必须为正,而不一定为1 但JDK中的一些可比较类正是以这种方式限制比较函数的输出,例如 scala> (65 to 90).map(n => java.lang.Integer.compare(77, n)) res19: IndexedSeq[Int] = Vector(1, 1, 1, 1, 1, 1, 1, 1,

ta
tb
相等时,
java.lang.Compariable
接口明确要求的唯一返回值是0。如果
a
小于
b
,则
比较(a,b)
必须为负数,不一定为负数−1和
比较(b,a)
必须为正,而不一定为1

但JDK中的一些可比较类正是以这种方式限制比较函数的输出,例如

scala> (65 to 90).map(n => java.lang.Integer.compare(77, n))
res19: IndexedSeq[Int] = Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)
而有些则没有,例如

scala> ('A' to 'Z').map(ch => java.lang.Character.compare('M', ch))
res10: IndexedSeq[Int] = Vector(12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 
-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13)
我很清楚RDN有点晦涩难懂(来自
javax.naming.ldap
),但我没想到会在
java.lang
中遇到这种情况。我首先注意到Java中Caesar cypher程序的
Character.compare()
中的无限制输出,但我发现在本地Scala REPL中运行类似的“实验”更容易

当我编写
分数
的实现时,我遵循了
整数
的示例,而不是
字符

scala> val fractA = new fractions.Fraction(65, 128)
fractA: fractions.Fraction = 65/128

scala> val fractB = new fractions.Fraction(90, 128)
fractB: fractions.Fraction = 45/64

scala> fractA to fractB
res20: fractions.FractionRange = 65/128 to 45/64

scala> val fractC = new fractions.Fraction(77, 128)
fractC: fractions.Fraction = 77/128

scala> res20.map(_.compare(fractC))
res21: IndexedSeq[Int] = Vector(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)

scala> res20.map(fractC.compare)
res22: IndexedSeq[Int] = Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)

scala> res19 == res22
res23: Boolean = true
分数
的情况下,像这样实现它是没有问题的。事实上,不这样做会更麻烦。分子和分母的类型为
long
,因此,如果差异的分子稍微超出
int
的范围,则可能会出现问题。通过使用
Long.signum()
,我将错误结果的可能性降低到一小部分边缘情况

由于
char
映射到
int
范围的一半,我认为
String
更容易不将
compare()
的结果限制为{−1,0,1}

scala> "Hello, World!" compare "Hello, world?"
res30: Int = -32
这里我猜测,如果没有涉及代理,或者即使涉及代理,也更容易在
字符串的每个字符上运行
Character.compare()
,直到第一个非零结果或到达末尾

这就是我们应该做最简单的事情并给出正确结果的解释吗?还是有更深层次的原因来限制某些可比类别的差异,而不是其他类别的差异?

主要答案 因为实现可以自由选择最适合自己的正值或负值。返回值如下所示:

负整数、零或正整数,因为此对象较小 大于、等于或大于指定对象

有人可能会争论这个规范是否是一个明智的决定,但这就是它的定义方式。因此,永远不要依赖于比较结果仅为-1、0或1,即使使用一个特定的Java版本进行实验表明这种行为——它可能会随着下一个版本而改变

实现答案 这个答案主要已经在你的问题中找到了

有两种典型的比较方法:

  • 整数减法:它给出的结果不限于-1、0、1。代码简单、优雅、快速。但也可能存在溢出,例如,对于值2_000_000_000-(-2_000_000_000)而言,数学上是4_000_000_000,但对于32位int,结果显示为-294_967_296,错误地暗示2_000_000小于-2_000_000。为了避免溢出,整数减法适用于大约+/-1_000_000_000的数字
  • 决策:如果。。。否则如果。。。else
构造,其中明确给出了三种情况的返回值。那么使用-1、0和1是很自然的选择,我不知道有哪个实现使用了其他固定值 所以,减法是字节和字符的有效解决方案,其中基于int的减法有足够的保留位,不会发生溢出。因此,这些数据类型及其派生类型更有可能显示-1、0和1之外的值

分数类 你正在写一个你正在实现的
分数
类。如果可以创建两个分数实例,而不给出异常,我将要求
compareTo()
方法始终给出正确的结果。由于分数的比较是一件棘手的事情,中间结果的溢出是可以预料的。因此,我建议创建一些分子和/或分母接近有效性限制的测试用例(无论您定义它们是什么)

另一种避免溢出的方法是切换到无限范围的
biginger
类型,但这可能会影响性能。

主要答案 因为实现可以自由选择最适合自己的正值或负值。返回值如下所示:

负整数、零或正整数,因为此对象较小 大于、等于或大于指定对象

有人可能会争论这个规范是否是一个明智的决定,但这就是它的定义方式。因此,永远不要依赖于比较结果仅为-1、0或1,即使使用一个特定的Java版本进行实验表明这种行为——它可能会随着下一个版本而改变

实现答案 这个答案主要已经在你的问题中找到了

有两种典型的比较方法:

  • 整数减法:它给出的结果不限于-1、0、1。代码简单、优雅、快速。但也可能存在溢出,例如,对于值2_000_000_000-(-2_000_000_000)而言,数学上是4_000_000_000,但对于32位int,结果显示为-294_967_296,错误地暗示2_000_000小于-2_000_000。为了避免溢出,整数减法适用于大约+/-1_000_000_000的数字
  • 决策:如果。。。否则如果。。。else
构造,其中明确给出了三种情况的返回值。那么使用-1、0和1是很自然的选择,我不知道有哪个实现使用了其他固定值 苏