【发布时间】:2013-11-01 02:48:55
【问题描述】:
我们的代码带有复杂的比较器,用于在整个应用程序中对 java 对象进行排序。从历史上看,这些都是有效的,但是自从在 Java 7 中引入 TimSort 以来,我们偶尔会遇到 Comparison 方法违反了它的一般约定! 错误.. 取决于对象中保存的数据。
这是我们的一个传统比较器的示例(可能已经有将近十年的历史了——请原谅我的狡猾):
public int compare(TemplateBean b1, TemplateBean b2) {
// avoid null pointer exceptions
if (b1 == null && b2 == null) return 0;
if (b1 == null) return 1;
if (b2 == null) return -1;
int cmp = 0;
if ("UNATTACHED".equals(b1.getStatusCode()) &&
!"UNATTACHED".equals(b2.getStatusCode())) {
cmp = 1;
}
if (!"UNATTACHED".equals(b1.getStatusCode()) &&
"UNATTACHED".equals(b2.getStatusCode())) {
cmp = -1;
}
if (!"UNATTACHED".equals(b1.getStatusCode()) &&
!"UNATTACHED".equals(b2.getStatusCode()) &&
!"FIELDSIMPLE".equals(b1.getRefRltshpTypeCode()) &&
!"FIELDSIMPLE".equals(b2.getRefRltshpTypeCode()) &&
!"CUSTOM".equals(b1.getRefRltshpTypeCode()) &&
!"CUSTOM".equals(b2.getRefRltshpTypeCode()) &&
!"FUNCTION".equals(b1.getRefRltshpTypeCode()) &&
!"FUNCTION".equals(b2.getRefRltshpTypeCode())) {
String parent1 = b1.getGroupCode() == null ? "" : b1.getGroupCode().toUpperCase();
String parent2 = b2.getGroupCode() == null ? "" : b2.getGroupCode().toUpperCase();
cmp = parent1.compareTo(parent2);
}
if (cmp == 0) {
Integer i1 = b1.getSortOrder() == null ? Const.ZERO : b1.getSortOrder();
Integer i2 = b2.getSortOrder() == null ? Const.ZERO : b2.getSortOrder();
cmp = i1.compareTo(i2);
}
if (cmp == 0) {
String s1 = b1.getShortDescription();
if (s1 == null) s1 = "";
String s2 = b2.getShortDescription();
if (s2 == null) s2 = "";
cmp = s1.compareToIgnoreCase(s2);
}
return cmp; }
所以,我想复制此功能,但使用一个可安全用于 TimSort 的 Comparator。
从代码中你可以看到这个比较有多个级别..
- 它将比较组代码。
- 如果组代码相同,它将比较排序顺序。
- 如果排序顺序相同,则会比较描述。
这意味着它将返回特定级别的比较结果。这可能是两个字符串或两个整数的比较结果。我认为这就是破坏 TimSort 的原因。
我能够让这个 Comparator 解决 General Contract 问题的唯一方法是对 bean 的内容进行哈希处理并执行字符串比较。其他想法包括编写我们自己的排序函数。肯定有更好的方法吗?
是否应该以另一种方式构造 bean 来支持这一点?
【问题讨论】:
-
我对 TimSort 不是很熟悉,但可能对您有所帮助的一件事是为您正在比较的字段实现某种枚举,而不是尝试执行复杂的字符串比较操作。我不确定这是否是解决此问题的好方法,但这是我的一个想法。
-
这里有太多的字符串比较。您真的应该将字符串转换为相应的数字或其他内容并进行比较,而不是试图挖掘这些混乱的条件。
-
我不认为问题与如何确定比较(使用字符串或枚举)有关,我认为这与 TimSort 具有此限制的事实有关:实现者还必须确保关系是可传递的: ((compare(x, y)>0) && (compare(y, z)>0)) 意味着 compare(x, z)>0。 这意味着如果 X 和 Y 在同一级别进行比较,而 Y 和 Z 在不同级别进行比较,则返回的整数可能会违反此规则。我需要返回一个代表顺序的值,但也不违反 X > Y > Z 规则。
-
乍一看,我会说问题出在第三个
if(不包括空检查)内。最后两个似乎没有问题,因为它们使用字符串和整数比较,并且这些比较已经是传递的。等我有时间再仔细看看。
标签: java sorting comparator