【问题标题】:Queue with multi-field sorting on priority and time按优先级和时间进行多字段排序的队列
【发布时间】:2015-04-10 16:30:38
【问题描述】:

意图

实现一个任务队列,排序基于 1) 优先级和 2) 时间,特别是任务创建时间或时间戳(未插入队列),优先考虑时间戳较旧的任务。

试过

这是我目前得到的;比我想象的要简单得多,因为它需要的不仅仅是一个 PriorityQueue。在比较器中,如果两个优先级相等,则对Task.time进行另一次比较,否则仅根据Task.priority进行比较。

import java.util.Comparator;
import java.util.PriorityQueue;

public class QueueWithPriorityAndTimeSort {
   enum TaskPriority {
      High, Medium, Low
   }

   class Task implements Comparable<Task> {   
      long time;
      TaskPriority priority;

      Task(TaskPriority p, long t) {      
         time = t;
         priority = p;
      }

      public int compareTo(Task task2) {
         return priority.compareTo(task2.priority);
      }
   }

   class HighPriorityWithTimeComparator implements Comparator<Task> {
      public int compare(Task task1, Task task2) {
         int compareResult = task1.compareTo(task2);
         if( compareResult == 0){           
            //same priority, now compare on time
            if( task2.time < task1.time)
               compareResult = 1;
            else 
               compareResult = -1;
         }                          
         return compareResult;
      }
   }

   public void buildAndTestQueue(){
      PriorityQueue<Task> queue = 
            new PriorityQueue<Task>(3, new HighPriorityWithTimeComparator());
      queue.add(new Task(TaskPriority.High, 9));
      queue.add(new Task(TaskPriority.Low, 7));
      queue.add(new Task(TaskPriority.Low, 3));
      queue.add(new Task(TaskPriority.High, 2));
      queue.add(new Task(TaskPriority.Medium, 5));
      queue.add(new Task(TaskPriority.Medium, 4));
      queue.add(new Task(TaskPriority.High, 6));
      queue.add(new Task(TaskPriority.High, 8));
      queue.add(new Task(TaskPriority.Low, 15));
      queue.add(new Task(TaskPriority.Low, 10));

      Task m = null;
      while ((m = queue.poll()) != null)
         System.out.println(
            String.format("Priority: %s, %d", m.priority, m.time));     
   }

   public static void main(String args[]) {
      QueueWithPriorityAndTimeSort queueTest = 
         new QueueWithPriorityAndTimeSort();
      queueTest.buildAndTestQueue();
   }
}

问题

这是一种有效的方法吗?我不相信添加的比较器逻辑会改变时间复杂度;除此之外,我没有看到任何明显的问题。

【问题讨论】:

  • 是的,这是正确的方法。您可以使用Long.compare() 来比较您的时间。您可以将您的 main 方法转换为适当的单元测试,以检查一切是否按预期工作。
  • 当然,这是一种有效的方法。唯一对我来说有点奇怪的是比较逻辑是如何在 Task 中的 compareTo 实现和外部比较器之间拆分的。为什么不把所有的比较逻辑放在一个或另一个中呢?自从我使用 PriorityQueue 已经有一段时间了,但是如果您的值类型实现 Comparable,那么 iirc PQ 将正常工作(即不提供 Comparator)。
  • 我很好奇,当您可以实现 Comparator 内部的所有逻辑时,为什么还要使用 Comparable 和 Comparator?
  • @wdf 考虑了自己,并决定根据上下文和用途拆分逻辑。基于优先级和时间的比较仅在Tasks 驻留在队列中时才相关,否则,时间并不那么重要。分离这些关注点允许根据特定需求进行排序以提高灵活性。
  • @raffian 这似乎有点过头了。但由于您的目标是拥有灵活的排序算法,请参阅我的答案。

标签: java queue priority-queue


【解决方案1】:

我的回答将针对您发表的以下评论:

分离这些关注点允许根据特定需求进行排序 增加灵活性

有更好的方法来分离关注点并使您的代码更加灵活,这样您就可以在不更改代码的情况下更改排序策略。听起来很令人兴奋?

让我们首先定义一个 Comparator 来比较优先级:

class HighPriorityComparator implements Comparator<Task> {
        public int compare(Task task1, Task task2) {
            return task1.priority.compareTo(task2.priority);
        }
    }

让我们定义一个 Comparator 来比较时间:

class TimeComparator implements Comparator<Task> {
        public int compare(Task task1, Task task2) {
            int compareResult = 0;
            if (task2.time < task1.time)
                compareResult = 1;
            else
                compareResult = -1;

            return compareResult;
        }
    }

现在是最好的部分。让我们定义一个比较器,它允许您混合和匹配比较逻辑。

class MixAndMatchComparator implements Comparator<Task> {

        List<Comparator<Task>> comparators;

        public MixAndMatchComparator(List<Comparator<Task>> comparators) {
            this.comparators=comparators;
        }

        @Override
        public int compare(Task o1, Task o2) {
            int compareResult = 0;
            for(Comparator<Task> comparator : comparators) {
                if(comparator.compare(o1, o2)!=0) {
                    return comparator.compare(o1, o2);
                }
            }
            return compareResult;
        }

    }

现在让我们按优先级排序,如果优先级相等,则按时间排序:

List<Comparator<Task>> comparators = new ArrayList<Comparator<Task>>();
//first sort on priority            
comparators.add(new HighPriorityComparator());
//then on time
comparators.add(new TimeComparator());
MixAndMatchComparator orComparator = new MixAndMatchComparator(comparators);
PriorityQueue<Task> queue = new PriorityQueue<Task>(3, orComparator);

等等!你改变主意了?您现在想按时间排序,如果时间相等,那么按优先级排序?只需更改将比较器添加到列表中的顺序:

List<Comparator<Task>> comparators = new ArrayList<Comparator<Task>>();
//first sort on time    
comparators.add(new TimeComparator());
//then on priority
comparators.add(new HighPriorityComparator());
MixAndMatchComparator orComparator = new MixAndMatchComparator(comparators);
PriorityQueue<Task> queue = new PriorityQueue<Task>(3, orComparator);

【讨论】:

  • 很有趣,我喜欢它...是的,我很兴奋:-D,顺便说一句,TimeComparator 中有一个错误,如果两次相等,结果将不会返回 0。
  • @raffian 我以为这就是你想要的?我所做的只是将您的代码移动到不同的类。该错误已经存在于您的代码中:)。我建议您接受我的回答,因为它不会比这更好! :)
  • 除非你对那个错误负责,否则我不接受深蹲:-D,j/k,
猜你喜欢
  • 2022-11-11
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 2016-09-23
  • 1970-01-01
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多