Java PriorityQueue删除任意元素的性能

时间:2009-04-16 15:53:57

标签: java performance collections queue

假设我有一个java PriorityQueue(java实现为堆),我根据某些条件迭代删除元素:

PriorityQueue q = new PriorityQueue();
...
Iterator it = q.iterator();
while(it.hasNext()){
    if( someCriterion(it.next()) )
        it.remove();
}

每个remove()操作需要多长时间?我不确定它是O(log(n))还是O(1)。

1 个答案:

答案 0 :(得分:10)

如果您正在使用Sun实施,则为O(log(n))来自Javadocs

  

实施说明:此实施提供    O(log(n))时间用于enqueing和dequeing方法    (offerpollremove()add);    remove(Object)contains(Object)的线性时间    方法;和检索方法的恒定时间    (peekelementsize)。

其他实现可能有不同的复杂性。


编辑: Javadocs不包括使用迭代器删除元素的性能,所以我不得不查找源代码。这与Sun实现有关,可能在Apple的版本,GNU Classpath等方面有所不同.Sun的源代码可用here;它也包含在JDK中,因此您可能已经安装了它。

PriorityQueue的迭代器中,remove()的默认情况是调用PriorityQueue.removeAt(lastRet),其中lastRetnext()上次返回的索引}。 removeAt()似乎是O(log(n))最坏的情况(它可能需要筛选队列,但不必迭代)。

然而,有时会发生不好的事情。来自removeAt()的评论:

/**
 * Removes the ith element from queue.
 *
 * Normally this method leaves the elements at up to i-1,
 * inclusive, untouched.  Under these circumstances, it returns
 * null.  Occasionally, in order to maintain the heap invariant,
 * it must swap a later element of the list with one earlier than
 * i.  Under these circumstances, this method returns the element
 * that was previously at the end of the list and is now at some
 * position before i. This fact is used by iterator.remove so as to
 * avoid missing traversing elements.
 */

removeAt()返回非null元素时,迭代器会将其添加到特殊队列中以供以后使用:当迭代器耗尽队列中的元素时,它会遍历此特殊队列。在迭代的第二阶段调用remove()时,迭代器调用PriorityQueue.removeEq(lastRetElt),其中lastRetElt是从特殊队列返回的最后一个元素。 removeEq被迫使用线性搜索来查找要删除的正确元素,这使其成为O(n)。但它可以使用==而不是.equals()来检查元素,因此其常数因子低于PriorityQueue.remove(Object)

因此,换句话说,使用迭代器进行删除在技术上是O(n),但实际上它应该比remove(Object)快得多。