工作中遇到的一个bug
Comparison method violates its general contract
这是一个站内通知消息的查询功能
消息分两种 如“原有代码”枚举中的 SINGLE单条/MULTIPLE聚合
规则: 单条读了的消息需要放到聚合消息的后面 单条没读的需要放到聚合消息的前面 聚合消息不用管读没读
线上用户反馈 站内通知消息打不开了
public enum NewsDisplayType {
SINGLE,
MULTIPLE;
}
private int newsV3Compare(NewsV3 o1, NewsV3 o2) {
NewsDisplayType displayType1 = o1.getDisplayType();
NewsDisplayType displayType2 = o2.getDisplayType();
if (!Objects.equals(displayType1, displayType2)) {
if (displayType1.equals(NewsDisplayType.MULTIPLE) && o2.getReadDateTime() != null) {
return -1;
}
return displayType1.compareTo(displayType2);
}
.............
}
线上通知消息数据粘到测试环境 换成测试用户id 调用接口
控制台打印了Comparison method violates its general contract!
根据栈调用信息定位到了news排序的method
参考了如下文章 了解到 Comparator 要满足自反性,传递性,对称性
https://blog.csdn.net/xdd19910505/article/details/124530002
https://blog.csdn.net/qq_39918677/article/details/120032419
然后 本地 idea 断点 逐行 debug
当执行到 return displayType1.compareTo(displayType2);
的时候 抛出了异常
看代码后发现
这里可能有四种case
case1: o1聚合 o2单条 o2读了 走 return -1 聚合的放前面
case2: o1聚合 o2单条 o2未读 走 return displaytype.compare 聚合的放后面
case3: o1单条 o2聚合 o1未读 走 return displaytype.compare 聚合的放后面
case4: o1单条 o2聚合 o1读了 走 return displaytype.compare 聚合的放后面
因为业务规则里面 是否阅读的规则排序优先于 聚合类型
所以 case4 有问题 违反了反对称性
case1和case4只是交换了o1,o2的顺序 结果出现了不一样的排序结果!!!
// 两个类型不同 单个未读的<聚合的<单个读了的
if (!Objects.equals(displayType1, displayType2)) {
if (displayType1.equals(NewsDisplayType.MULTIPLE)) {
if (o2.getReadDateTime() != null) {
// o2/单个 读了 o1/聚和 在前面
return -1;
} else {
// o2/单个 没读 o1/聚和 在后面
return displayType1.compareTo(displayType2);
}
}else{
if (o1.getReadDateTime() != null) {
// o1/单个 读了 放后面
return 1;
} else {
// o1/单个 未读 放前面
return displayType1.compareTo(displayType2);
}
}
}
https://blog.csdn.net/xdd19910505/article/details/124530002
https://blog.csdn.net/qq_39918677/article/details/120032419