`PriorityQueue` 中的 `add` 和 `addAll` 行为不同,这是为啥呢?

Posted

技术标签:

【中文标题】`PriorityQueue` 中的 `add` 和 `addAll` 行为不同,这是为啥呢?【英文标题】:`add` and `addAll` from a `PriorityQueue` behave differently, why is this?`PriorityQueue` 中的 `add` 和 `addAll` 行为不同,这是为什么呢? 【发布时间】:2020-05-26 07:12:56 【问题描述】:

在将元素从 PriorityQueue 传输到 ArrayList 时,我注意到 List.addList.addAll 的行为不同。但我不知道为什么。

示例代码:

  public static void main(String[] args) 
    PriorityQueue<String> heap = new PriorityQueue<>(Comparator.reverseOrder());
    heap.add("a");
    heap.add("aa");
    heap.add("aaa");

    ArrayList<String> listAddAll = new ArrayList<>();
    listAddAll.addAll(heap);
    System.out.println(listAddAll);

    ArrayList<String> listAdd = new ArrayList<>();
    while (!heap.isEmpty())
      listAdd.add(heap.remove());
    
    System.out.println(listAdd);
  

listAddAll 包含 [aaa, a, aa],而 listAdd 包含 [aaa, aa, a]。我希望两者都是后者,因为这是比较器指定的顺序。

【问题讨论】:

对不起,我的错,看错了。 你观察到的不是addaddAll之间的区别,而是traversing the queue和反复调用remove()之间的区别。 【参考方案1】:

因为ArrayList.addAll 被实现为像这样使用Collection.toArray

public boolean addAll(Collection<? extends E> c) 
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;

PriorityQueue.toArray只是底层数组的复制操作:

public Object[] toArray() 
        return Arrays.copyOf(queue, size);

因此,addAll 将根据队列元素的顺序将其插入到 queue 字段中,该字段是平衡二叉堆,将元素的顺序保留为树而不是列表。你得到的原始数组代表树而不是排序列表。

【讨论】:

以上是关于`PriorityQueue` 中的 `add` 和 `addAll` 行为不同,这是为啥呢?的主要内容,如果未能解决你的问题,请参考以下文章

Java Review - PriorityQueue源码解读

priorityQueue 堆

Java的优先队列PriorityQueue详解

Java 集合深入理解 :优先队列(PriorityQueue)之源码解读,及最小顶堆实现研究

CommonsCollections4分析

java - 为啥在java中的poll方法之后PriorityQueue中的值会发生变化? [复制]