通过多个字段对JavaBean进行排序的正确方法
我们的代码带有复杂的比较器,用于在整个应用程序中对java对象进行排序。从历史上看,这些方法都是有效的,但是自从在Java7中引入TimSort之后,我们偶尔会发现比较方法违反了它的一般约定错误。。取决于对象中保存的数据 下面是我们的一个遗留比较工具的示例(可能已经有十年历史了——请原谅这种不可靠的做法): 所以,我想复制这个功能,但是使用一个比较器,可以安全地与TimSort一起使用 从代码中可以看出,此比较有多个级别通过多个字段对JavaBean进行排序的正确方法,java,sorting,comparator,Java,Sorting,Comparator,我们的代码带有复杂的比较器,用于在整个应用程序中对java对象进行排序。从历史上看,这些方法都是有效的,但是自从在Java7中引入TimSort之后,我们偶尔会发现比较方法违反了它的一般约定错误。。取决于对象中保存的数据 下面是我们的一个遗留比较工具的示例(可能已经有十年历史了——请原谅这种不可靠的做法): 所以,我想复制这个功能,但是使用一个比较器,可以安全地与TimSort一起使用 从代码中可以看出,此比较有多个级别 它将比较组码 如果组码相同,则会比较排序顺序 如果排序顺序相同,它将比较描
bean是否应该以另一种方式构造以支持这一点?上述
比较器的主要问题是它不是可传递的。它似乎在较旧的JDK上“起作用”,因为它们没有提供对损坏的比较器的检测,但它在一般情况下无法正常工作,直到JDK 7才发现错误行为
它的非传递性来源于groupCode
属性上的条件比较。
考虑当比较器将对象A和B作为 > SoTrOrth< /COD>字段省略比较<代码>组代码< /代码>,因为<代码>“函数”。
由于sortOrder
,对象B和C按BgroupCode
比较,直接比较A和C时,A和C可能被排序为C比较器
的传递性要求
要解决此问题,应始终考虑groupCode
,并且由于refRltshpTypeCode
值而跳过groupCode
的每个对象都应被视为小于任何现在使用groupCode
进行比较的对象
比较方法应该是这样的(这只是给你一个想法):
在哪里
来自@RomanKonovai的消息是正确的,但是添加了更多细节
考虑代码如何比较这三个对象,并假设所有非引用:
A B C
Status UNATTACHED UNATTACHED UNATTACHED
RefRltshpType CUSTOM FUNCTION CUSTOM
Group Cat Ball Apple
SortOrder 10 20 30
通过问题中的实现,我们可以看到AA,或者A
。这显然是不符合逻辑的,因为排序顺序取决于状态
和RefRltshpType
的值,由组
或排序器
决定,这两者之间没有任何联系。本质上,这意味着排序顺序未定义,因为结果完全取决于输入的顺序,即sort(sort(List))
可能不会给出与sort(List)
相同的结果
解决此问题的方法是执行以下操作:
private int objectCompare(String allowed, Comparable v1, Comparable v2) {
if (v1 == v2) return 0;
if (v1 == null) return 1;
if (v2 == null) return -1;
boolean c1 = v1.equals(allowed);
boolean c2 = v2.equals(allowed);
return c1 ? c2 ? 0 : 1 : c2 ? -1 : 0;
}
private int objectCompare(Comparable v1, Comparable v2) {
if (v1 == v2) return 0;
if (v1 == null) return 1;
if (v2 == null) return -1;
return v1.compare(v2);
}
public int compare(TemplateBean b1, TemplateBean b2) {
// avoid null pointer exceptions
if (b1 == b2) return 0;
if (b1 == null) return 1;
if (b2 == null) return -1;
int cmp = objectCompare("UNATTACHED", b1.getStatusCode(), b2.getStatusCode());
if (cmp == 0) {
cmp = objectCompare("FIELDSIMPLE", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare("CUSTOM", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare("FUNCTION", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare(b1.getGroupCode(), b2.getGroupCode());
if (cmp == 0) {
cmp = objectCompare(b1.getSortOrder(), b2.getSortOrder());
if (cmp == 0) {
cmp = objectCompare(b1.getShortDescription(), b2.getShortDescription());
}
}
}
}
}
}
return cmp;
}
我对TimSort不太熟悉,但有一件事可能会对您有所帮助,那就是为要比较的字段实现某种枚举,而不是尝试执行复杂的字符串比较操作。我不确定这是否是一个解决这个问题的好方法,但这是我的一个想法。这里有太多的字符串比较。你真的应该把字符串转换成相应的数字或其他东西,并对它们进行比较,而不是试图去挖掘这些混乱的条件。我认为问题不在于如何确定比较(使用字符串或枚举),我认为这更多地与TimSort有这个限制的事实有关:实现者还必须确保关系是可传递的:((compare(x,y)>0)和&(compare(y,z)>0))意味着compare(x,z)>0。这意味着,如果X和Y在同一级别进行比较,但Y和Z在不同级别进行比较,则返回的整数可能会违反此规则。我需要返回一个表示顺序的值,但也不违反X>Y>Z规则。乍一看,问题出在第三个if
(不包括空检查)中的某个地方。最后两个似乎没有问题,因为它们使用字符串和整数比较,而这些比较已经是可传递的。等我有更多的时间,我会仔细看的。谢谢你的解释。
private static boolean shouldBeComparenByGroupCode(TemplateBean b1) {
return !"UNATTACHED".equals(b1.getStatusCode()) &&
!"FIELDSIMPLE".equals(b1.getRefRltshpTypeCode()) &&
!"CUSTOM".equals(b1.getRefRltshpTypeCode()) &&
!"FUNCTION".equals(b1.getRefRltshpTypeCode());
}
A B C
Status UNATTACHED UNATTACHED UNATTACHED
RefRltshpType CUSTOM FUNCTION CUSTOM
Group Cat Ball Apple
SortOrder 10 20 30
private int objectCompare(String allowed, Comparable v1, Comparable v2) {
if (v1 == v2) return 0;
if (v1 == null) return 1;
if (v2 == null) return -1;
boolean c1 = v1.equals(allowed);
boolean c2 = v2.equals(allowed);
return c1 ? c2 ? 0 : 1 : c2 ? -1 : 0;
}
private int objectCompare(Comparable v1, Comparable v2) {
if (v1 == v2) return 0;
if (v1 == null) return 1;
if (v2 == null) return -1;
return v1.compare(v2);
}
public int compare(TemplateBean b1, TemplateBean b2) {
// avoid null pointer exceptions
if (b1 == b2) return 0;
if (b1 == null) return 1;
if (b2 == null) return -1;
int cmp = objectCompare("UNATTACHED", b1.getStatusCode(), b2.getStatusCode());
if (cmp == 0) {
cmp = objectCompare("FIELDSIMPLE", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare("CUSTOM", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare("FUNCTION", b1.getRefRltshpTypeCode(), b2.getRefRltshpTypeCode());
if (cmp == 0) {
cmp = objectCompare(b1.getGroupCode(), b2.getGroupCode());
if (cmp == 0) {
cmp = objectCompare(b1.getSortOrder(), b2.getSortOrder());
if (cmp == 0) {
cmp = objectCompare(b1.getShortDescription(), b2.getShortDescription());
}
}
}
}
}
}
return cmp;
}