Java:优先级队列实现可按正确顺序迭代

时间:2013-04-03 08:17:42

标签: java iterator priority-queue

我正在寻找Java中PQ的一些实现,它允许以PQ顺序迭代 - 首先是元素,接下来是下一个等等。我尝试使用TreeSet(实现NavigableSet)但它会导致一个问题。就我而言:

  • 我正在使用Comparator作为我的对象
  • 由于某些外部操作而导致的优先级更改
  • 如果优先级发生变化我知道哪个对象,但我不知道它是先前的优先级

作为最后一点的结果 - 当我想更新其优先级时,我无法在TreeSet中找到我的元素:/ 你碰巧知道:聪明的方式服从这个吗?或者是以“好”方式迭代的PQ的一些实现?或者我应该创建一些链接数据结构来匹配对象与它们在树中的位置?

更新:

  • 并发不是问题
  • 对象无法从TreeSet中删除,因为它的优先级已更改,因此Comparator将进行不同的评估,并且在此数据结构中将找不到对象。插入不是问题。
  • 我无法使用compareTo方法,因为此优先级不是比较这些对象的正确方法。这就是我需要使用Comparator
  • 的原因

可能的解决方案:

  • 创建类PrioritizedObject,将按优先级进行比较并保留我的对象
  • 使用地图:我的对象 - > PrioritizedObject
  • PrioritizedObject保留在某些NavigableSet

我会使用此地图从NavigableSet中删除对象。如果我添加一些内容,当然会用新元素更新它。 问题是我必须从此NavigableSet包装迭代器以获取迭代器返回我的对象​​。

有没有更好的解决方案?

3 个答案:

答案 0 :(得分:0)

我建议使用ConcurrentSkipListSet而不是TreeSet,因为它是线程安全的。如果您知道优先级正在发生变化的对象,则可以调用remove(objToChange),更改其优先级,然后将其重新添加到集合中。

要非常小心地将equalshashcodecompareTo方法依赖于可变字段的任何对象添加到集合中。

编辑:我认为任何解决方案最终都会看起来像PrioritizedObject这对我来说似乎没问题。如果要迭代对象,请使用Map.keySet

答案 1 :(得分:0)

如果并发不是问题,那么您需要做的就是在更新元素的优先级后立即重新排序树。如果我理解问题,这个草图应该适合你。

示例元素:

public class Element implements Comparable<Element> {

private final Integer id;
private Integer priority;

public Element(Integer id, Integer priority) {
    this.id = id;
    this.priority = priority;
}

@Override
public String toString() {
    return "Element{" + "id=" + id + ", priority=" + priority + '}';
}

public Integer getPriority() {
    return priority;
}

public void setPriority(Integer priority) {
    this.priority = priority;
}

@Override
public int compareTo(Element o) {
    if (o == null) {
        throw new NullPointerException();
    }
    return priority.compareTo(o.priority);
}
}

草图:

public class Tree {

public static TreeSet<Element> priorityQueue = new TreeSet<Element>();

public static void dump(TreeSet<Element> in) {
    for (Element e : in) {
        System.out.println(e);
    }

}

public static void updatePriority(Element e, int newPriority) {
    if (priorityQueue.remove(e)) {
        e.setPriority(newPriority);
        priorityQueue.add(e);
    }
}

public static void main(String[] args) {

    int id;
    Element lastElement = null;
    for (int i = 0;i < 10 ; i++) {
        id = (int)(Math.random()*1000);
        priorityQueue.add(lastElement = new Element(id, id));
    }
    dump(priorityQueue);
    updatePriority(lastElement, 0);
    System.out.println("updating "+lastElement+ " priority to 0");
    dump(priorityQueue);

}
}

您可以通过从treeset中删除元素,设置新优先级然后重新插入来更新元素。此方案的更新操作的复杂性为2*O(log(n)) = O(log(n))

更新:

我能理解的最好的是:你有两个标准需要排序/索引。当我遇到同样的问题时,我使用了这个approach,但this是一个非常有趣的方法,我强烈建议您阅读。

答案 2 :(得分:0)

  

如果优先级发生变化我知道哪个对象,但我不知道它是先前的优先级

您无需了解其先前的优先级。您所要做的就是将其删除并重新插入。