【问题标题】:Java compareTo method failsJava compareTo 方法失败
【发布时间】:2020-02-05 01:33:56
【问题描述】:

我有一个名为 task 的类,我想将它放入 PriorityQueue。

我的班级在 date 和一个名为 isUrgent

的布尔字段上具有可比性
 @Override
        public int compareTo(Task task) {
            int x = 0;
            if (!isUrgent && task.isUrgent)
                x=1;
            else if (isUrgent && !task.isUrgent)
                x=-1;
            else return  date.compareTo(task.date);

            return x +date.compareTo(task.date);
        }

第一次使用 Comparables,当我从优先级队列中删除一个任务时,它应该在最近的日期之前删除,但如果它是紧急的,那么它应该被删除第一个紧急任务。

但我在删除过程中得到了这个,

Task{isUrgent=true, date=Sat Apr 04 00:00:00 BST 2020}
Task{isUrgent=true, date=Sat Apr 04 00:00:00 BST 2020}
Task{isUrgent=false, date=Sat Apr 04 00:00:00 BST 2020}
Task{isUrgent=true, date=Thu Apr 04 00:00:00 BST 2030}
Task{isUrgent=false, date=Sat Apr 04 00:00:00 BST 2020}
Task{isUrgent=true, date=Thu Apr 04 00:00:00 BST 2030}
Task{isUrgent=false, date=Thu Apr 04 00:00:00 BST 2030}
Task{isUrgent=false, date=Thu Apr 04 00:00:00 BST 2030}

我在 compareTo 方法中做错了什么?

【问题讨论】:

  • 是的,但我也改变了,没有最后一个 else 语句。它保持不变
  • @ScaryWombat 如果满足前 2 个条件中的任何一个,它将通过最后一次返回。设置x=-1/0/1 然后添加它很奇怪。 compareTo 只使用 0 - 不是特定值。
  • @ScaryWombat 别担心 - 我也有同样的想法,不得不眯着眼睛看了几秒钟 :-)

标签: java priority-queue


【解决方案1】:

您似乎想要的是先按紧迫性比较任务,然后按日期比较。您应该将结果链接起来,而不是将两个比较器的结果相加,这样只有在两个任务的紧急程度相同(即两者都是紧急的,或者都是非紧急的)时才比较两个任务的日期。

幸运的是,Comparator 类有一些有用的方法,可以很容易地创建一个比较器来满足你的需求。大多数时候,包括在您的用例中,您实际上不需要编写自己的 compareTo 方法。您可以使用comparing 方法按紧急程度或日期进行比较,也可以使用thenComparing 将它们链接在一起。 reversed 方法允许您比较紧急情况,以便 true 出现在 false 之前。

Comparator<Task> cmp =
    Comparator.comparing(t -> t.isUrgent).reversed().thenComparing(t -> t.date);

或者使用方法引用(如果你的类有getter方法):

Comparator<Task> cmp =
    Comparator.comparing(Task::isUrgent).reversed().thenComparing(Task::getDate);

然后您可以通过调用适当的PriorityQueue constructor 来创建使用此比较器的优先级队列:

PriorityQueue<Task> queue = new PriorityQueue<>(cmp);

【讨论】:

  • 太棒了!不知道我们可以在 java 中链接这些方法。我之前在自定义比较器中尝试过比较器(将其传递给 PriorityQueue 的构造函数),但使用的是 compare 方法,这给了我一些错误。但这对于其他人来说更容易和更一致。反过来的方法也是我以前需要的。非常感谢@kaya3
【解决方案2】:

通过返回x + date.compareTo(task.date),您没有为urgent 标志赋予足够的权重。

如果x 为-1,并且日期比较的结果为1,则返回零。

此外,不能保证日期比较会返回 -1、0 或 1(尽管初步测试表明确实如此)。 Javadoc 只是声明:

回报:
如果参数 Date 等于此 Date,则值为 0;如果此 Date 在 Date 参数之前,则值小于 0;和一个 如果此 Date 在 Date 参数之后,则值大于 0。

一个简单的解决方法是:

@Override
public int compareTo(Task task) {
    if (!isUrgent && task.isUrgent)
        return 1;
    else if (isUrgent && !task.isUrgent)
        return -1;

    return date.compareTo(task.date);
}

【讨论】:

  • 好的,对。我认为它总是会返回 -1、0 或 1。感谢您的澄清。在不重视紧急标志的情况下,您还有其他方法可以做到这一点吗?
  • 我认为您接受的答案中提出的解决方案很好。我在回答中也为您的代码添加了修复程序。
  • 我赞成这个答案,因为它解释了为什么添加两个比较器的结果通常不起作用,它解决了您确实希望您的类实现 Comparable(而不是使用比较器)的情况),尽可能干净。
  • 我赞成这些答案并接受罗比给出的第二个答案。第一个声明清楚了我在函数中使用 2 个比较器的问题。也很容易修复,工作,但它不会与发现紧急任务的日期进行比较,这就是我尝试使用 x 重量的原因。另一个答案用更清洁的方法清楚地说明了这一点。谢谢你们俩的回答。
  • @andrecj 从逻辑上讲,两种解决方案的行为完全相同并且会产生相同的排序结果,但我绝对没有问题你更喜欢另一种而不是我的 :)
猜你喜欢
  • 2017-04-19
  • 1970-01-01
  • 2012-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
相关资源
最近更新 更多