考虑以下compareTo方法,实现Comparable<T>
接口
@Override
public int compareTo(MyObject o)
{
if (o.value.equals(value)
return 0;
return 1;
}
令人惊讶的是,程序员实现了compareTo,就好像它是equals()一样。显然是个错误。我预计这会导致Collections.sort()崩溃,但事实并非如此。相反,它只会给出一个任意的结果:排序的结果取决于初始排序。
public class MyObject implements Comparable<MyObject>
{
public static void main(String[] args)
{
List<MyObject> objects =
Arrays.asList(new MyObject[] {
new MyObject(1), new MyObject(2), new MyObject(3)
});
Collections.sort(objects);
System.out.println(objects);
List<MyObject> objects2 =
Arrays.asList(new MyObject[] {
new MyObject(3), new MyObject(1), new MyObject(2)
});
Collections.sort(objects2);
System.out.println(objects2);
}
public int value;
public MyObject(int value)
{
this.value = value;
}
@Override
public int compareTo(MyObject o)
{
if (value == o.value)
return 0;
return 1;
}
public String toString()
{
return "" + value;
}
}
结果:
[3, 2, 1]
[2, 1, 3]
我们能为compareTo的这个奇怪的实现想出一个用例吗?或者它总是无效的。在后者的情况下,它应该抛出异常,或者甚至不编译吗?
它没有崩溃或引发异常的原因。
当你实现这个方法时,你必须履行合同,但如果你不履行,这只意味着你会从任何依赖它的东西中得到任意的结果。没有什么会特意检查你的实现的正确性,因为这只会减慢一切。
排序算法的效率是根据它进行的比较次数来定义的。这意味着它不会仅仅为了检查您的实现是否一致而添加额外的比较,就像HashMap
会对所有内容调用.hashcode()
两次,只是为了检查两次都给出相同的结果一样。
如果在排序过程中发现问题,则可能引发异常;但不要依赖它。
违反Comparable
或Comparator
的合同不一定会导致异常。sort
方法不会花费额外的精力来检测这种情况。因此,它可能会导致顺序不一致、结果明显正确或引发异常。
实际行为取决于输入数据和当前实现。例如,Java 7在其sort
实现中引入了TimSort
,这比早期Java版本的实现更有可能为不一致的Comparable
或Comparator
实现抛出异常。这可能会发现在使用以前的Java版本时仍然未检测到的错误,然而,这并不是一个有助于调试的功能,它只是更复杂的优化的副作用。
请注意,对于像您的问题中这样的简单情况,编译器或代码审计工具检测比较方法的不对称行为并非完全不可能。然而,据我所知,还没有编译器自动执行这样的检查。如果您希望安全起见,您应该始终为实现Comparable
或Comparator
的类实现单元测试。
根据文档,Collections.sort()使用了一种合并排序的变体,它将列表划分为多个子列表,然后重复合并这些列表;排序部分是在这些列表的合并期间完成的;如果您的比较方法是任意的,那么将以任意的顺序进行合并。
根据你想要表示的数学组或顺序,它可能是一个有效的情况,因此没有理由抛出任何错误。然而,您展示的示例并不能代表您所知道的标准数字的自然顺序。
提交的订单不是全部订单。
===编辑===
感谢您的评论,事实上,规范不允许使用compareTo方法来实现非完全或非反对称阶。