[Java container source code] PriorityQueue source code analysis

1. Structure

PriorityQueue inheritance relationship, core member variables and main constructor:

// 可以看到 PriorityQueue 只是一个普通队列,并不是一个阻塞队列
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
    
    
	
	// 通过数组保存队列数据,队列节点是Object
	// 这里采用的其实是堆这种数据结构,后面元素的排序也是采用的堆排序
	transient Object[] queue;
	
	transient int modCount = 0; 
	private int size = 0
	
	// 比较器,priortyqueue是优先队列,所以比较器是必须的
	private final Comparator<? super E> comparator;
	
	// 数组默认初始容量
	private static final int DEFAULT_INITIAL_CAPACITY = 11;
	// 数组最大容量
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	//----------------------------------构造函数------------------------------
	// 构造函数一:空参构造
	public PriorityQueue() {
    
    
		// 默认初始容量,无比较器
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    
    // 构造函数二:传入自定义比较器
    public PriorityQueue(Comparator<? super E> comparator) {
    
    
        // 默认初始容量,自定义比较器
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }
    
    // 构造函数三:传入指定容量和比较器
    public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
    
    
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        // 数组初始化
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
}

1.1 Introduction to Heap

The heap is logically a complete binary tree, so data can be stored through an array, while the rest of the trees mostly use a chain structure for data storage

  • Heap classification:
    • Big top heap: The big top heap means that the parent node is the largest in any (child) tree
    • Small top heap: Small top heap means that no matter in any (child) tree, the parent node is the smallest. PriortyQueue uses a small top heap
  • Two operations on the heap:
    • Floating: generally used to balance the heap after adding new elements to the heap
    • Sinking: Generally used to balance the pile after removing the top of the pile and changing the tail to the top of the pile
  • Heap sorting: using the characteristics of the large top heap and the small top heap, the top of the heap is constantly removed, and the elements removed are the highest value of the elements in the heap, and then the heap is balanced

Here is only a brief introduction. Students who want to understand the code implementation of the heap and more operations can refer to [Data Structure] Java Implementation of Heap: Various Heap Operations & Heap Sorting .

1.2 Comparable vs. Comparator

Let me talk about the Comparable interface and the Comparator interface. Both of them provide a way to compare different objects under the same type, so here we mainly focus on their differences:

  • Comparable interface
    • Generally used as an internal comparator , the entity object needs to implement the Comparable interface and override the compareTo method
    • Call the compareTo method of the entity object when comparing
public interface Comparable<T> {
    
    
    public int compareTo(T o);
}
  • Comparator interface
    • Generally used as an external comparator , write the implementation class in advance and rewrite the compare method, and then pass it as a parameter to the method that requires the comparator.
    • When compared directly to the comparator passed two objects to be compared. Here is an example:
// Comparator作为参数,匿名类,省略实现
// Dog:要比较的类型
Collections.sort(list, new Comparator<Dog>() {
    
    
        // 重写compare方法,参数是两个待比较的对象
        public int compare(Dog o1, Dog o2) {
    
    
        // 比较策略
        return o2.age - o1.age;
        }
});

Note here that because Comparator is an interface, it cannot be new, but it can be used as an anonymous class which is equivalent to omitting the implementation class.

2. Method analysis & api

2.1 Join the team

add()

public boolean add(E e) {
    
    
    	// 调用offer
        return offer(e);
}

offer()

The offer method will first determine whether it needs to be expanded, and then according to whether the queue is determined to empty the element

public boolean offer(E e) {
    
    
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
    	// 超出容量,扩容
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1; // size++
    	
        if (i == 0)
        	// 如果队列为空直接将元素放到队首
            queue[0] = e;
        else
            // 上浮
            // 可以这么理解,队列(数组)中所有元素已经是排好序的,而新元素会追加到数组末尾,所以为了保证总体有序,可能需要将新元素上浮(前移)
            siftUp(i, e);
        return true;
}

siftUp ()

Determine whether to use a custom comparator Comparator or the compareTo method of the object entity when comparing

private void siftUp(int k, E x) {
    
    
        if (comparator != null)
        	// 有自定义比较器,就用自定义比较器,调用 siftUpUsingComparator
            siftUpUsingComparator(k, x);
        else
        	// 无自定义比较器,就用元素的Comparable方法,调用 siftUpComparable
            siftUpComparable(k, x);
}

siftUpComparable

The compareTo method of the entity object is used to compare to complete the floating of new elements, so that the overall order of the queue (array) is: small => large. Please see the notes for the specific process:

// k:当前队列实际大小的位置(没有新元素),x:要插入的元素
private void siftUpComparable(int k, E x) {
    
    
    	// 将x强转为Comparable,可以调用CompareTo比较
        Comparable<? super E> key = (Comparable<? super E>) x;
    	    
    	// 通过不断与父节点进行比较,最后找到正确的位置
        while (k > 0) {
    
    
            
            int parent = (k - 1) >>> 1; // 得到k的父节点索引,即对 k 进行减倍
            Object e = queue[parent]; // 得到父节点的数据
            
            if (key.compareTo((E) e) >= 0) // 如果 x 比 parent 大,表示当前位置就是正确的位置
                break;
                
            queue[k] = e;  // x 比 parent 小,则将parent交换到x的位置
            k = parent; // 继续上浮,直到找到一个比 x 小的 parent
        }
        queue[k] = key; // 已将找到了合适的位置,将 x 放入
}

2.2 Departure: poll

Dequeueing is actually returning the leader of the team, that is, the top of the small top pile, and then recovering the pile through the sinking operation.

public E poll() {
    
    
        if (size == 0)
            return null;
        int s = --size; // size--
        modCount++;
    	// 取队首
        E result = (E) queue[0];
        
        // 保存数组最后一个元素,也即堆尾
        E x = (E) queue[s];
        // 将堆尾删除
        queue[s] = null;
        if (s != 0)
        	// 下沉(将堆尾x放到堆顶位置,然后下沉)
            siftDown(0, x);
        return result;
}

2.3 Get the leader of the team: peek

public E peek() {
    
    
    	// 直接将arr[0]返回
        return (size == 0) ? null : (E) queue[0];
}

2.4 Expansion: grow

The essence of expansion is array copy, so the question is how big the new array is:

  • oldCap < 64 —> newCap = oldCap*2 + 2
  • oldCap >= 64 —> newCap = oldCap * 2
  • newCap > Max —> newCap = Integer.MAX_VALUE
private void grow(int minCapacity) {
    
    
        int oldCapacity = queue.length;
        // newCap
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        // 防止溢出,即超出最大容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
    	// 将拷贝后的大数组再赋给queue
        queue = Arrays.copyOf(queue, newCapacity);
}

Guess you like

Origin blog.csdn.net/weixin_43935927/article/details/108858762