【问题标题】:java queue with dynamic priority flag具有动态优先级标志的java队列
【发布时间】:2017-07-03 10:22:26
【问题描述】:

我需要构建一个队列,默认情况下将按时间顺序添加和删除元素。但是,如果客户端为队列设置了优先级标志,我需要能够根据元素的优先级顺序拉取元素。

我正在考虑创建一个由映射支持的优先级队列,该映射按优先级顺序跟踪队列索引,并且基于优先级标志,我可以从映射中提取项目并从队列中的索引中弹出项目。

但是,使用这种方法,问题是,天气我默认创建地图或仅在设置标志的情况下创建地图(考虑到动态创建地图的成本很高,我倾向于默认使用它)。

如果有更好的方法或者是否存在现有的实现,请告诉我。

这是我目前拥有的:

import javax.naming.OperationNotSupportedException;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class DynamicPriorityQueue<ComparableQueueElement> implements IQueue<ComparableQueueElement> {

    private static final int CONSTANT_HUNDRED = 100;
    private boolean fetchByCustomPriority = false;
    private final ReentrantLock lock;
    private final PriorityQueue<ComparableQueueElement> queue;
    private final PriorityQueue<ComparableQueueElement> customPriorityQueue;

    public DynamicPriorityQueue() {
        this(null);
    }

    public DynamicPriorityQueue(Comparator<ComparableQueueElement> comparator) {
        this.lock = new ReentrantLock();
        this.queue = new PriorityQueue<>(CONSTANT_HUNDRED);
        if (comparator != null)
            this.customPriorityQueue = new PriorityQueue<ComparableQueueElement>(CONSTANT_HUNDRED, comparator);
        else
            this.customPriorityQueue = null;
    }

    public void setFetchByCustomPriority(boolean fetchByCustomPriority) throws OperationNotSupportedException {
        if (this.customPriorityQueue == null)
            throw new OperationNotSupportedException("Object was created without a custom comparator.");

        this.fetchByCustomPriority = fetchByCustomPriority;
    }

    public void push(ComparableQueueElement t) throws InterruptedException {
        if (this.lock.tryLock(CONSTANT_HUNDRED, TimeUnit.MILLISECONDS)) {
            try {
                this.queue.offer(t);
                if (this.customPriorityQueue != null)
                    this.customPriorityQueue.offer(t);
            } finally {
                this.lock.unlock();
            }
        }
    }

    public ComparableQueueElement peek() {
        return this.fetchByCustomPriority ? this.queue.peek()
                : (this.customPriorityQueue != null ? this.customPriorityQueue.peek() : null);
    }

    public ComparableQueueElement pop() throws InterruptedException {
        ComparableQueueElement returnElement = null;
        if (this.lock.tryLock(CONSTANT_HUNDRED, TimeUnit.MILLISECONDS)) {
            try {
                if (this.fetchByCustomPriority && this.customPriorityQueue != null) {
                    returnElement = this.customPriorityQueue.poll();
                    this.queue.remove(returnElement);
                }
                else {
                    returnElement = this.queue.poll();
                    if (this.customPriorityQueue != null) {
                        this.customPriorityQueue.remove(returnElement);
                    }
                }
            } finally {
                this.lock.unlock();
            }
        }
        return returnElement;
    }
}

【问题讨论】:

  • 谢谢你。但是我的问题是,如果设计是最优的,还是我会继续使用另一种数据结构/方法?
  • 如果优先级队列中的所有项目都具有相同的优先级,则队列推送/弹出应该非常有效,在 O(1) 时间内。
  • 您是否需要一个容器,消费者可以从中选择是按优先顺序删除下一个元素还是按 FIFO 顺序删除下一个元素?
  • 如果您的项目队列不是很大,只需根据客户的偏好重新排列队列即可。也就是说,使用不同的排序比较函数创建一个新队列。此偏好多久更改一次,您的队列中有多少项目?

标签: java multithreading data-structures priority-queue


【解决方案1】:

我在重读问题后删除了我的 cmets,它可能会变得复杂。您需要将 fifo(按时间顺序)队列转换为带有标志的优先级队列。您的地图需要进行排序并且能够保存重复值。否则,您将需要搜索地图以找到最高优先级或搜索队列。我不会这么做的。

编辑

如何使用包装类:

class Pointer<T>{
    T element
}

还有两个指针队列,队列共享指针但返回的指针不同?您唯一需要做的就是检查“元素”是否不为空(当它离开其中一个队列时将其设置为空。

指针引用保留在另一个队列中,但您在返回之前检查是否为空。

编辑

您的代码没有地图。

public ComparableQueueElement peek() {
        return this.fetchByCustomPriority ? this.queue.peek()
                : (this.customPriorityQueue != null ? this.customPriorityQueue.peek() : null);
    }

不正确。如果它不是自定义的,你应该从 this.queue 中偷看

编辑

请注意,通过使用包装类,您可以将删除调用保存在另一个队列中。唯一增加的开销是在获取时需要检查 null

【讨论】:

  • 如何创建具有优先级有序链表索引映射的链表。让我试试看。
  • 您需要考虑相反的情况。如果标志被切换回来。这意味着您需要准备好两个队列。
  • 我认为地图会杀了你,因为当设置标志时,你需要每次都找到正确的优先级或有多个地图(每个优先级)
【解决方案2】:

如果您的应用程序要求标志非常频繁地更改,那么对我来说,实现看起来不错。在这种情况下,您的两个队列都准备好从队列中提供或轮询对象。尽管它使您的添加操作繁重,但检索速度很快。

但是如果这些更改不频繁,那么您可以考虑仅在标志更改时从 FIFO 队列重新初始化自定义优先级队列。 并且只在一个队列而不是两个队列上执行所有的 peek、offer 操作。 如果使用 FIFO 优先级,效率会更高。 如果你这样做修改你的推送操作如下 -

public void push(ComparableQueueElement t) throws InterruptedException {
    if (this.lock.tryLock(CONSTANT_HUNDRED, TimeUnit.MILLISECONDS)) {
        try {
            this.queue.offer(t);
            if (this.fetchByCustomPriority) // add to customPriorityQueue only when flag is enabled
                this.customPriorityQueue.offer(t);
        } finally {
            this.lock.unlock();
        }
    }
}

【讨论】:

  • 谢谢普里亚。我了解与添加操作相关的成本。然而,优先级的切换可能经常发生,因此有两个前置队列。
猜你喜欢
  • 2011-01-18
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多