Java的Comparable接口返回值必须是精确的1、-1或0吗?

4
这可能是一个琐碎的问题,但我没有找到任何关于它的信息,所以在这里提出:
当实现Comparable接口时,我们应该定义compareTo()方法,根据文档,以下条件应为真:
  • 对于所有的x和y,sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。

  • 关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0)意味着x.compareTo(z)>0。

  • x.compareTo(y)==0 意味着对于所有z,sgn(x.compareTo(z)) == sgn(y.compareTo(z))。

现在,让人困惑的部分是返回值,如下所示:

如果此对象小于指定对象,则返回负整数,如果相等,则返回零,如果大于指定对象,则返回正整数。

尽管没有要求限制返回值的方式,但大多数实现似乎返回1-10
因此,以下代码适用于排序包含类Foo实例的列表(使用Collections.sort())。
public int compareTo(Foo other){
    return this.value > other.value? 1 : this.value < other.value ? -1 : 0;
}

这个却不行:
public int compareTo(Foo other){
    return (int)(this.value - other.value);
}

其中value是一个long类型的数值,它们之间的差值不超过Integer.MAX_VALUE

我是否漏掉了什么,或者返回值必须精确地为1-10,这是否与文档相矛盾?

更新:感谢大家的回答,但似乎人为因素在这里起到了作用。我提到计算出的差值小于Integer.MAX_VALUE,这应该意味着没有溢出,但我的计算是错误的,所以实际上我确实发生了溢出,这导致了奇怪的结果。

3个回答

8
合同非常灵活,允许使用this.value - other.value这种习惯用语(后来由于整数溢出而被证明是不正确的)。但在某些情况下仍然有价值,比如str.length() - str2.length()。字符串或数组大小比较不会溢出,因为最小长度为0,最大长度为Integer.MAX_VALUE0 - Integer.MAX_VALUE仍大于Integer.MIN_VALUE),所以在需要按长度/大小排序时很方便。
与0比较(大于/小于)通常比与1/-1比较更快/生成更小的字节码/汇编代码,那么为什么要限制用户呢?您完全可以使用任何正/负值。

没有这样的限制,你可以返回任何你想要的负数或正数,参考Effective Java。 - Amir Pashazadeh
将此标记为解决方案,因为它是我找到实际问题的关键。 - Jave

1

不,你可以返回任何整数。你具体遇到了什么错误?

请测试以下类:

  public static void main(String[] args) {

        final ToSort sort0 = new ToSort(-100);
        final ToSort sort1 = new ToSort(1);
        final ToSort sort2 = new ToSort(100);


        List<ToSort> elements = new ArrayList<ToSort>(){{add(sort2); add(sort1); add(sort0);}};
        System.out.println("Unsorted:" + elements.toString());

        Collections.sort(elements);

        System.out.println("Sorted:" + elements.toString());

    }

    static class ToSort implements Comparable{

        long value;
        public ToSort(long value){
            this.value = value;
        }

        @Override
        public int compareTo(Object other) {
            return (int) (this.value - ((ToSort)other).value);
        }

        public String toString(){
            return ""+value;
        }
    }

我得到了以下的输出:
run:
Unsorted:[100, 1, -100]
Sorted:[-100, 1, 100] 
BUILD SUCCESSFUL (total time: 0 seconds)

你可能想在你的compareTo方法中添加一个断点并进行调试,以检查是否运行了你所期望的内容。


没有错误,只是列表没有正确排序。双重换行是复制粘贴错误。 - Jave
new Long(1).longValue()1L,因此 (int)(new Long(1).longValue() - other) 等同于 1 - (int) other - Peter Lawrey
@PeterLawrey 确实是这样,那只是为了演示目的 - 已经编辑过了,但 (int) (long-long)((int) long) - ((int) long) 给出相同的结果,对吗? - Marcelo
谢谢您的回答,我遇到的问题是由于长整型数值之间的巨大差异导致了整数溢出。 - Jave
1
@Java 你能举个例子吗?我敢打赌你做不到。 ;) - Peter Lawrey
1
@PeterLawrey:看来你赢了这个赌注。我以为将长整型转换为整型(或任何其他原始类型转换为较少位数的类型)只是截断结果。今天学到了很多!:p - Jave

-1

Javadocs 还说:

在上述描述中,符号 sgn(expression) 表示数学符号函数,其定义为根据表达式的值是负数、零还是正数返回 -1、0 或 1 中的一个

编辑:

是的,我误解了。你是对的。


是的,但从未提到您必须在结果上使用 sgn - Jave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接