【问题标题】:Collections.sort() Comparison method violates its general contract in Java [duplicate]Collections.sort() 比较方法违反了它在 Java 中的一般合同 [重复]
【发布时间】:2018-07-17 11:14:38
【问题描述】:

我知道这类问题已经被问过数百万次,甚至数十亿次,但我还没有找到答案:)

这个compare() 方法没有LongDoubleFloat、...,它只有DatebooleanNull 检查器,但是它告诉我contract violation error,有谁能帮忙吗?

Collections.sort(users, new Comparator<MiniUser>() {
        @Override
        public int compare(MiniUser u1, MiniUser u2) {
            boolean resComing = checkMatchConditions(u1,user);
            boolean resExists = checkMatchConditions(u2,user);

            if(Boolean.valueOf(resComing) && Boolean.valueOf(resExists)) {
                if(u1.getLastMatchDate() == null){
                    return -1;
                }else if(u2.getLastMatchDate() ==null ){
                    return 1;
                }else if (u1.getLastMatchDate().toInstant().isBefore(u2.getLastMatchDate().toInstant())){
                    return -1;
                }else {
                    return 1;
                }
            }

            else if (Boolean.valueOf(resComing)) {
                return -1;
            }
            return 1;
        }

    });

MiniUser.class

public class MiniUser implements Serializable {

    String id;
    String name;
    Date lastMatchDate;
    boolean showCompleteName;

//getters, setters
}

checkMatchConditions根据一些计算返回布尔值

【问题讨论】:

  • 你认为你为什么需要Boolean.valueOf(resComing)等?他们会立即被resComing拆箱。
  • user 在你的 Comperator 中定义在哪里?
  • 这显然违反了约定,因为它在与自身比较时不会返回 0(如果 resComingresExists 都是 false)。
  • 注意:if(Boolean.valueOf(resComing) &amp;&amp; Boolean.valueOf(resExists)) if(resComing &amp;&amp; resExists) 相同

标签: java sorting collections comparator code-contracts


【解决方案1】:

您应该首先阅读JavaDoc of Comparator.compare() 以了解该“合同”是什么:

实施者必须确保 sgn(compare(x, y)) == -sgn(compare(y, x)) 对于所有 x 和 y。

通常来说,如果“x 大于 y,y 必须小于 x”。听起来很明显,但在您的比较器中并非如此:

  • 在您的情况下,当两个用户拥有checkMatchConditions false 时,您就违反了它,在这种情况下,compare(u1, u2)compare(u2, u1) 都返回 1。因此,在某些情况下,u1 大于 u2,而u2 大于u1,属于违规。
  • 同样,如果两个用户都有checkMatchConditionstrue,并且他们的lastMatchDates都是null,他们也将违反合同。
  • 此外,由于您手动尝试将日期与isBefore 进行比较,因此当两个用户的checkMatchConditions true 和他们的lastMatchDates 相等时,您也会在这两种情况下返回-1。

为了解决这个问题,您应该首先添加一个自然语言描述,说明您希望如何订购用户。然后你就可以算出比较器的逻辑了。

顺便说一下,错误与Boolean.valueOf()无关。


既然您已经解释了您想如何订购,请看一下这个比较器:

public int compare(MiniUser u1, MiniUser u2)
{
    // order by match
    boolean u1Matches = checkMatchConditions(u1, user);
    boolean u2Matches = checkMatchConditions(u2, user);

    if (u1Matches != u2Matches)
    {
        // put matching ones first
        return u1Matches ? -1 : 1;
    }
    else if (u1Matches)
    {
        // order by dates
        boolean u1HasDate = u1.getLastMatchDate() != null;
        boolean u2HasDate = u2.getLastMatchDate() != null;

        if (u1HasDate != u2HasDate)
        {
            // put the ones without date first
            return u1HasDate ? 1 : -1;
        }
        else if (u1HasDate)
        {
            // order chronologically
            return u1.getLastMatchDate().compareTo(u2.getLastMatchDate());
        }
        else
        {
            // no dates, no order possible
            return 0;
        }
    }
    else
    {
        // both don't match, no order possible
        return 0;
    }
}

如果我正确理解了您的要求,这应该对您的元素施加一致的顺序。请注意我如何使用DatecompareTo 进行日期排序而不是自己进行,以及我如何返回0,以防它们在订单方面“相等”而不是“随机”返回1-1

【讨论】:

  • 非常感谢 Malte,我想在这里对用户进行排序的方式是,如果 resComing && resExists 为 true,则根据日期对它们进行排序,或者如果 resComing 为 true 而 resExists 为 false,则将用户resComing first,除了我不在乎,所以它的所有关于添加更多的条件?
  • @TamerSaleh 即使你不在乎是 a > b 还是 a
  • @TamerSaleh 看看我添加的比较器。如果我正确理解您的解释,它应该一致。尝试了解它如何改进对特殊情况的处理,尤其是当两个用户应该被视为相等时(即“我不在乎这些是如何排序的”)。
  • @MalteHartwig 非常感谢,我现在正在尝试这段代码 :)
  • @MalteHartwig 我试过你的代码,还是同样的错误:/
【解决方案2】:

您需要找到sgn(compare(x, y)) == -sgn(compare(y, x)) 不存在的地方。建议你用蛮力找例子。

Comparator<MiniUser> comp = ...
for (MiniUser a : users) {
    for (MiniUser b: users) {
         if (a == b) continue;
         if (comp.compare(a, b) != -comp.compare(b, a)) {
            // print an error message with these two.
         }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    相关资源
    最近更新 更多