PriorityQueue的源码分析

基于jdk1.8源码进行分析的。

上面刚分析了优先阻塞队列,是基于二叉堆实现的,下面我们看一下优先队列的实现。

类继承结构

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable 

继承了AbstractQueue列,实现了Serializable接口。

成员属性

    //默认初始化大小
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    //操作堆的数组
    transient Object[] queue;
    //优先队列中元素的个数
    private int size = 0;
    //元素比较器
    private final Comparator<? super E> comparator;
    //修改次数
    transient int modCount = 0; // non-private to simplify nested class access

构造方法

PriorityQueue()

    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

默认构造方法,初始化大小为11,调用内部其他构造方法实现的。

PriorityQueue(int initialCapacity)

    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

可以指定容量大小,调用内部其他构造方法实现的。

PriorityQueue(Comparator<? super E> comparator)

    public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }

指定比较器的构造方法,默认初始化容量大小为11,调用其他内部构造方法实现。

PriorityQueue(int initialCapacity,Comparator<? super E> 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;
    }

以上三个都是调用该构造方法实现的。

PriorityQueue(Collection<? extends E> c)

@SuppressWarnings("unchecked")
    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
            initFromCollection(c);
        }
    }

通过结合进行初始化,默认调用上面的构造方法实现的。

核心方法

add(E e)

添加元素

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

通过源码我们可以看到是通过调用内部的offer方法实现的。

offer(E e)

队列添加元素具体实现

    public boolean offer(E e) {
        //添加元素为空,直接抛出异常
        if (e == null)
            throw new NullPointerException();
        //动态维护修改次数
        modCount++;
        //记录当前优先队列中元素葛素
        int i = size;
        //如果满了,就需要扩容
        if (i >= queue.length)
            //扩容调用方法,后面分析
            //参数size+1
            grow(i + 1);
        //动态维护size
        size = i + 1;//i=size
        //如果size
        if (i == 0)
            //队列为空,添加第一个元素,也就是数组第一个元素
            queue[0] = e;
        else
            //动态添加元素,并维护堆特性
            siftUp(i, e);
        //添加成功
        return true;
    }

grow(int minCapacity)

用于实现堆扩容操作。 

    //参数size+1
    private void grow(int minCapacity) {
        //当前队列长度
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        // 如果比较小,可以看到是和64相比较,扩展为两倍
        // 否则,增长为1.5倍
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        // overflow-conscious code
        //扩容后数据校验,如果不合法,调用其他方法实现,稍后分析
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //拷贝数组
        queue = Arrays.copyOf(queue, newCapacity);
    }

里面涉及到方法 hugeCapacity,源码如下:

 hugeCapacity(int minCapacity)

    private static int hugeCapacity(int minCapacity) {
        //size+1<0
        //数据合法性校验
        if (minCapacity < 0) // overflow
            //抛出异常
            throw new OutOfMemoryError();
        //返回较大值
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

下面我们看看,如果不需要扩容,或者扩容完毕,且优先队列中有元素的时候的操作siftUp是这么实现的。

siftUp(int k, E x)

    //参数:i,e
    //添加元素具体实现
    private void siftUp(int k, E x) {
        //比较器为空
        if (comparator != null)
            //没有比较器具体实现方法
            siftUpUsingComparator(k, x);
        else
            //指定比较器具体实现方法
            siftUpComparable(k, x);
    }

我们继续往下看调用到的方法。

siftUpUsingComparator(int k, E x)

指定比较器的时候,调用的方法。

    //参数:i=size,e
    //这个过程叫做在算法我们都知道成为上滤操作
    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        //size>0
        //队列有元素
        while (k > 0) {
            //获取父节点索引
            int parent = (k - 1) >>> 1;
            //获取父节点元素
            Object e = queue[parent];
            //比较器比较元素大小
            //这个比较器是构造方法传入的,用户自定义,如果符合条件,直接跳出循环 
            if (comparator.compare(x, (E) e) >= 0)
                break;
            //调整,满足二叉堆性质
            //父子交换,小的元素往前放,也就是树的顶端位置方向
            queue[k] = e;
            k = parent;
        }
        //调整完毕,将对应k位置的索引在数组中的位置指定位元素x,也就是要添加的元素
        queue[k] = x;
    }

siftUpComparable(int k, E x)

没有指定比较器的时候调用的方法

    //参数:i=size,e
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        //题外话:补充个知识点
        //<? extends E> 只能是E或者E的子类,上限
        //<? super E>   只能是E或者E的附列,下限
        //这里操作是一个强转类型的操作
        Comparable<? super E> key = (Comparable<? super E>) x;
        //size>0
        //优先队列有元素
        while (k > 0) {
            //查找父元素索引
            int parent = (k - 1) >>> 1;
            //父元素对应的元素
            Object e = queue[parent];
            //进行比较器比较
            if (key.compareTo((E) e) >= 0)
                //如果父元素小于子元素,跳出循环
                break;
            //父子交换,继续循环
            queue[k] = e;
            k = parent;
        }
        //如果队列没有元素,第一个元素直接初始化
        queue[k] = key;
    }

上面整个思路还是比较清晰的,首先进行数据合法性校验,然后如果需要扩容就进行动态扩容,扩容完毕后,根据是否指定有构造器,来分别用不同的方法进行处理,动态维护二叉堆的特性,两个方法实现思路大体类似。下面我们来看看取出元素操作。

poll()

方法用于取出元素。

    public E poll() {
        //优先队列没有元素
        if (size == 0)
            //返回null
            return null;
        //队列有元素,动态维护size
        int s = --size;
        //维护修改次数
        modCount++;
        //队列首元素,或者说是数组首元素
        E result = (E) queue[0];
        //s=--size
        //数组最后一个元素
        E x = (E) queue[s];
        //队列最后索引位置置空
        queue[s] = null;
        //s=--size
        //队列中不只一个元素的时候,如果有一个,移除后数组为空,那么不需要执行,直接返回结果
        if (s != 0)
            //进行下滤操作
            siftDown(0, x);
        //返回队列首元素
        return result;
    }

下面我们看一下对应源码中调用到的其他方法的源码。

siftDown(int k, E x)

执行删除元素具体实现。

    //参数:0,x
    private void siftDown(int k, E x) {
        //如果指定了构造器
        if (comparator != null)
            //调用该方法 
            siftDownUsingComparator(k, x);
        else
            //如果没有指定构造器,那么调用该方法
            siftDownComparable(k, x);
    }
    //参数:0,x
    @SuppressWarnings("unchecked")
    private void siftDownUsingComparator(int k, E x) {
        //half=size/2
        int half = size >>> 1;
        //遍历整体数组的一半元素
        //如果k小于half,则k位置的元素就不是叶子节点
        while (k < half) {
            //寻找孩子节点的索引
            int child = (k << 1) + 1;
            //孩子节点处对应的元素
            Object c = queue[child];
            //右孩子的索引
            int right = child + 1;
            //数组没有遍历完毕,且左孩子大于右孩子
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                //更新c为右孩子对应节点的元素,寻找右孩子最小的元素
                c = queue[child = right];
            //如果要添加的元素小于左孩子
            if (comparator.compare(x, (E) c) <= 0)
                //循环结束
                break;
            //队尾元素比根元素孩子都大,则需要"下移"
            //交换根元素和孩子c的位置
            queue[k] = c;
            //将根元素位置k指向最小孩子的位置,进入下层循环
            k = child;
        }
        //找到队尾元素x的合适位置k之后进行赋值
        queue[k] = x;
    }

siftDownComparable(int k, E x) 

过程和上面类似,区别在于没有指定比较器的时候调用该方法。 

    //参数: 0,x
    @SuppressWarnings("unchecked")
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

此处不再介绍和粘贴peek和indexOf等方法源码,下面直接看一下remove方法的源码实现。

remove(Object o) 

    public boolean remove(Object o) {
        int i = indexOf(o);
        if (i == -1)
            return false;
        else {
            removeAt(i);
            return true;
        }
    }

溢出元素,首先找到o对应的下表的位置。如果没有找到,直接返回-1,否则调用removeAt方法实现。

removeAt(int i)

    private E removeAt(int i) {
        // assert i >= 0 && i < size;
        //维护修改次数
        modCount++;
        //size-1
        int s = --size;
        //如果移除的是最后一个元素
        if (s == i) // removed last element
            //最后一个元素置空
            queue[i] = null;
        else {
            //记录最后一个元素
            E moved = (E) queue[s];            
            //最后一个元素置空
            queue[s] = null;
            //进行下滤操作
            siftDown(i, moved);
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }

以上是整个优先队列的源码分析过程,如果有不对的地方还请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值