学习JDK8源码之--PriorityQueue

1. PriorityQueue简介

  PriorityQueue是一种优先队列,不同于普通队列的先进先出原则,优先队列是按照元素的优先级出列,每次出列都是优先级最高的元素。优先队列的应用很多,最典型的就是线程了,例如守护线程(GC)就是优先级比较低的一个线程。

  PriorityQueue底层是通过堆(完全二叉树)这种数据结构来存储数据的,每次出列的元素都是堆中最小的(最小堆),判断元素大小的依据由使用者指定,相当于指定优先级。

  文章参考自:https://www.cnblogs.com/tstd/p/5125949.html

2. PriorityQueue继承关系

  PriorityQueue继承自AbstractQueue,实现了java.io.Serializable接口。

  AbstractQueue实现了Queue接口,并对队列的基本方法进行了实现。

  实现了 java.io.Serializable 接口:可以启用其序列化功能,能通过序列化去传输。

3. PriorityQueue实现

1. 核心参数

    //定义了底层数组的最大长度
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //比较器,通过比较器确定优先级
    private final Comparator<? super E> comparator;
    //修改次数,不可被序列化
    transient int modCount = 0; // non-private to simplify nested class access
    //Object数组用来存储数据,不可被序列化
    transient Object[] queue; // non-private to simplify nested class access
    //默认初始容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    //存储的元素个数
    private int size = 0;

    private static final long serialVersionUID = -7720805057305804111L;

  从上面可以看出,PriorityQueue底层是通过数组进行数据存储的,可是为什么说是通过堆来存储的呢?因为这是基于数组实现的二叉堆,对于数组中任意位置的n上元素,其左孩子在[2n+1]位置上,右孩子[2(n+1)]位置,它的父亲则在[(n-1)/2]上,而根的位置则是[0]。

leftNo = parentNo*2+1

rightNo = parentNo*2+2

parentNo = (nodeNo-1)/2

2. 构造函数

    //无参构造,默认初始容量11,比较器为空,这里要求入队的元素必须实现Comparator接口
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    //传入一个初始容量,比较器为空,这里要求入队的元素必须实现Comparator接口
    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

    //传入一个比较器,默认初始容量11
    public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }

    //传入初始容量和比较器
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

    //传入一个集合
    public PriorityQueue(Collection<? extends E> c) {
        // 如果集合c是包含比较器Comparator的(SortedSet/PriorityQueue),则使用集合c的比较器来初始化队列的Comparator
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
             // 从集合c中初始化数据到队列
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();        
             // 从优先队列c中初始化数据到队列
            initFromPriorityQueue(pq);
        }
        //  如果集合c没有包含比较器,则默认比较器Comparator为空
        else {
            this.comparator = null;
             // 从集合c中初始化数据到队列
            initFromCollection(c);
        }
    }

    //传入一个优先队列
    public PriorityQueue(PriorityQueue<? extends E> c) {
        //使用传入队列的比较器,若是比较器为空,则需要队列内的元素实现Comparable接口
        this.comparator = (Comparator<? super E>) c.comparator();
        //从优先队列c中初始化数据到队列
        initFromPriorityQueue(c);
    }

    //传入一个可排序的set
     public PriorityQueue(SortedSet<? extends E> c) {
        //使用传入set的比较器,若是比较器为空,则需要队列内的元素实现Comparable接口
        this.comparator = (Comparator<? super E>) c.comparator();
         // 从集合c中初始化数据到队列
        initElementsFromCollection(c);
    }

3. 核心方法

    //传入一个优先队列,并把当前优先队列的内容替换
    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
        //如果该优先队列是PriorityQueue,则直接把底层数组和大小属性替换
        if (c.getClass() == PriorityQueue.class) {
            this.queue = c.toArray();
            this.size = c.size();
        //否则调用根据集合的统一初始化方式
        } else {
            initFromCollection(c);
        }
    }
    
    //传入一个集合,并把当前优先队列的内容替换
    private void initElementsFromCollection(Collection<? extends E> c) {
        //把传入的集合转化为数组a
        Object[] a = c.toArray();
        //如果该集合内的元素不是Object类型,则拷贝一份
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, a.length, Object[].class);
        int len = a.length;
        //遍历数组,若其中存在null元素则抛出空指针异常
        if (len == 1 || this.comparator != null)
            for (int i = 0; i < len; i++)
                if (a[i] == null)
                    throw new NullPointerException();
        //将底层数组和大小属性替换
        this.queue = a;
        this.size = a.length;
    }
    
    //传入一个集合,并把当前优先队列的内容替换,然后将数组调整为一个最小堆(时间复杂度o(n))
    private void initFromCollection(Collection<? extends E> c) {
        initElementsFromCollection(c);
        //将数组调整为一个最小堆
        heapify();
    }
    
    //对底层数组进行扩容
    private void grow(int minCapacity) {
        //取当前数组的容量
        int oldCapacity = queue.length;
        //如果当前数组容量小于64则扩容1倍,否则扩容0.5倍,防止数据量太大时扩容太多造成空间浪费
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        //当扩容后的容量大于定义的最大值时的处理
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //通过System.arraycopy进行扩容
        queue = Arrays.copyOf(queue, newCapacity);
    }
    
    //大容量处理
    private static int hugeCapacity(int minCapacity) {
        //超过int最大值,抛出内存溢出异常
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
        //小于int最大值大于定义的最大值,返回int最大值;小于定以的最大值,返回定义的最大值
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    //向队列中添加元素
    public boolean add(E e) {
        return offer(e);
    }
    
    //按照最小堆的规则添加元素
    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;
        //若队列大小为0,则直接插入根位置
        if (i == 0)
            queue[0] = e;
        //否则根据元素的大小插入合适的位置,保证最小堆的结构
        else
            siftUp(i, e);
        return true;
    }
    
    //获取队列根元素的值
    public E peek() {
        return (size == 0) ? null : (E) queue[0];
    }
    
    //获取队列中指定元素的索引位置,通过equals比较判断,不存在返回-1
    private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        return -1;
    }
    
    //将指定元素移除队列
    public boolean remove(Object o) {
        int i = indexOf(o);
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
    }
    
    //移除指定元素,仅同包类可访问
    boolean removeEq(Object o) {
        for (int i = 0; i < size; i++) {
            if (o == queue[i]) {
                removeAt(i);
                return true;
            }
        }
        return false;
    }
    
    //判断队列中是否包含指定元素
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }
    
    //通过System.arraycopy将队列转为Object数组返回
    public Object[] toArray() {
        return Arrays.copyOf(queue, size);
    }
    
    //通过System.arraycopy将队列转为指定类型的数组返回
    public <T> T[] toArray(T[] a) {
        final int size = this.size;
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(queue, size, a.getClass());
        System.arraycopy(queue, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    
    //获取一个迭代器(内部类实现的)
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    //返回队列的大小
    public int size() {
        return size;
    }
    
    //清空队列,通过for循环遍历将所有元素置为null
    public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            queue[i] = null;
        size = 0;
    }
    
    //将队列根元素取出,并重新调整最小堆结构
    public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }
    
    //移除指定位置的元素
    private E removeAt(int i) {
        // assert i >= 0 && i < size;
        modCount++;
        int s = --size;
        //移除最后一个元素,直接移除即可
        if (s == i)
            queue[i] = null;
        //否则需要重新调整最小堆的结构
        else {
            //取最后一个元素moved
            E moved = (E) queue[s];
            //将最后一个元素置为空
            queue[s] = null;
            //把moved元素从i位置开始,根据最小堆的结构向下调整
            //最小堆是父节点小于等于任意一个子节点所以moved元素可能比i位置的子节点的值大
            //这时候就需要把moved元素跟其中一个比较小的子节点交换位置,以此类推,直到满足最小堆的结构
            siftDown(i, moved);
            //在这之前,只是把队列的最后一个元素置为空并赋值给moved变量,所以这时候i位置的元素还未动过
            //当i位置的值与moved值相等时,
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }
    
    //向上调整最小堆结构,k插入的位置,x为插入元素
    private void siftUp(int k, E x) {
        //如果指定了比较器,则使用指定比较器的规则调整
        if (comparator != null)
            siftUpUsingComparator(k, x);
        //否则使用元素默认的比较规则
        else
            siftUpComparable(k, x);
    }
    
    //默认比较规则上移
    private void siftUpComparable(int k, E x) {
        // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
        Comparable<? super E> key = (Comparable<? super E>) x;
        //不是根节点都需要进行调整处理
        while (k > 0) {
            //取k节点的父节点
            int parent = (k - 1) >>> 1;
            //获取父节点元素e
            Object e = queue[parent];
            //如果插入的元素大于等于父节点,则满足最小堆的规则,终止调整,否则需要把插入的元素上移一位
            if (key.compareTo((E) e) >= 0)
                break;
            //插入的元素小于父节点,将父元素放入插入元素的位置
            queue[k] = e;
            //将插入位置调整为父节点的位置
            k = parent;
        }
        //直到调整完成后,再将要插入的元素插入最终确定的位置
        queue[k] = key;
    }
    
    //使用指定比较器的规则上移
    private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            //取k节点的父节点
            int parent = (k - 1) >>> 1;
            //获取父节点元素e
            Object e = queue[parent];
            //如果插入的元素大于等于父节点,则满足最小堆的规则,终止调整,否则需要把插入的元素上移一位
            if (comparator.compare(x, (E) e) >= 0)
                break;
            //插入的元素小于父节点,将父元素放入插入元素的位置
            queue[k] = e;
            //将插入位置调整为父节点的位置
            k = parent;
        }
        //直到调整完成后,再将要插入的元素插入最终确定的位置
        queue[k] = x;
    }
    
    //向下调整最小堆结构,k插入的位置,x为插入元素
    private void siftDown(int k, E x) {
        //如果指定了比较器,则使用指定比较器的规则调整
        if (comparator != null)
            siftDownUsingComparator(k, x);
        //否则使用元素默认的比较规则
        else
            siftDownComparable(k, x);
    }
    
    //使用元素默认的比较规则下移,k插入的位置,x为插入元素
    private void siftDownComparable(int k, E x) {
        // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
        Comparable<? super E> key = (Comparable<? super E>)x;
        //这里主要是去掉所有的叶子节点(没有子节点),排除不需要下移的情况,当k<half说明k节点有子节点
        int half = size >>> 1;     
        while (k < half) {
            //取k节点的左子节点
            int child = (k << 1) + 1; 
            //取k节点的左子节点元素c
            Object c = queue[child];
            //取k节点的右子节点
            int right = child + 1;
            //k<half说明k节点有子节点,但不一定有两个,即一定有左子节点,不一定有右子节点
            //right < size说明k节点是有两个节点的,当左节点大于右节点时,将右节点的值赋给c,即找出k的最小子节点
            if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            //要插入的元素小于子节点的最小节点,则终止调整,否则需要下移
            if (key.compareTo((E) c) <= 0)
                break;
            //k的最小子节点赋给k
            queue[k] = c;
            //将插入的置为调整为最小子节点的位置
            k = child;
        }
        //调整完成后,将要插入的元素放入调整后的位置
        queue[k] = key;
    }
    
    //使用指定比较器的比较规则下移,k插入的位置,x为插入元素
    private void siftDownUsingComparator(int k, E x) {
    //这里主要是去掉所有的叶子节点(没有子节点),排除不需要下移的情况,当k<half说明k节点有子节点
        int half = size >>> 1;
        while (k < half) {
            //取k节点的左子节点
            int child = (k << 1) + 1;
            //取k节点的左子节点元素c
            Object c = queue[child];
            //取k节点的右子节点
            int right = child + 1;
            //k<half说明k节点有子节点,但不一定有两个,即一定有左子节点,不一定有右子节点
            //right < size说明k节点是有两个节点的,当左节点大于右节点时,将右节点的值赋给c,即找出k的最小子节点
            if (right < size && comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            //要插入的元素小于子节点的最小节点,则终止调整,否则需要下移
            if (comparator.compare(x, (E) c) <= 0)
                break;
            //k的最小子节点赋给k
            queue[k] = c;
            //将插入的置为调整为最小子节点的位置
            k = child;
        }
        //调整完成后,将要插入的元素放入调整后的位置
        queue[k] = x;
    }
    
    //调整整个最小堆的结构
    private void heapify() {
        //因为调用的是下移的操作,所以需要去掉所有的叶子节点
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }
    
    //返回指定的比较器
    public Comparator<? super E> comparator() {
        return comparator;
    }
    
    //序列化队列
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out element count, and any hidden stuff
        s.defaultWriteObject();

        // Write out array length, for compatibility with 1.5 version
        s.writeInt(Math.max(2, size + 1));

        // Write out all elements in the "proper order".
        for (int i = 0; i < size; i++)
            s.writeObject(queue[i]);
    }
    
    //反序列化序列
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }
    
    

猜你喜欢

转载自www.cnblogs.com/despacito/p/10845316.html