PriorityQueue的使用与底层源码

一.PriorityQueue的使用

PriorityQueue   —— 优先级队列,基于优先对实现的无界队列

Java默认实现基于小根堆实现;

 

常用方法:

 PriorityQueue<Integer> que = new PriorityQueue<>();
        que.add(1);
        que.add(2);
        que.add(3);//队头增添元素
        System.out.println(que.toString());//打印
        System.out.println(que.contains(2));//判断是否存在
        que.offer(2);//队尾增添元素
        System.out.println(que.toString());
        System.out.println(que.peek());//获取队头元素
        que.poll();//删除元素
        que.remove();//删除元素
        System.out.println(que.toString());
        que.clear();//清空队列
        System.out.println(que.toString());

二.PriorityQueue底层源码实现:

1.构造函数:

/**
     * 默认构造方法,使用默认的初始大小来构造一个优先队列,比较器comparator为空,这里要求入队的元素必须实现Comparator接口
     */
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

    /**
     * 使用指定的初始大小来构造一个优先队列,比较器comparator为空,这里要求入队的元素必须实现Comparator接口
     */
    public PriorityQueue( int initialCapacity) {
        this(initialCapacity, null);
    }

    /**
     * 使用指定的初始大小和比较器来构造一个优先队列
     */
    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
        // 初始大小不允许小于1
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        // 使用指定初始大小创建数组
        this.queue = new Object[initialCapacity];
        // 初始化比较器
        this.comparator = comparator;
    }

    /**
     * 构造一个指定Collection集合参数的优先队列
     */
    public PriorityQueue(Collection<? extends E> c) {
        // 从集合c中初始化数据到队列
        initFromCollection(c);
        // 如果集合c是包含比较器Comparator的(SortedSet/PriorityQueue),则使用集合c的比较器来初始化队列的Comparator
        if (c instanceof SortedSet)
            comparator = (Comparator<? super E>)
                ((SortedSet<? extends E>)c).comparator();
        else if (c instanceof PriorityQueue)
            comparator = (Comparator<? super E>)
                ((PriorityQueue<? extends E>)c).comparator();
        //  如果集合c没有包含比较器,则默认比较器Comparator为空
        else {
            comparator = null;
            // 调用heapify方法重新将数据调整为一个二叉堆
            heapify();
        }
    }

    /**
     * 构造一个指定PriorityQueue参数的优先队列
     */
    public PriorityQueue(PriorityQueue<? extends E> c) {
        comparator = (Comparator<? super E>)c.comparator();
        initFromCollection(c);
    }

    /**
     * 构造一个指定SortedSet参数的优先队列
     */
    public PriorityQueue(SortedSet<? extends E> c) {
        comparator = (Comparator<? super E>)c.comparator();
        initFromCollection(c);
    }
 
    /**
     * 从集合中初始化数据到队列
     */
    private void initFromCollection(Collection<? extends E> c) {
        // 将集合Collection转换为数组a
        Object[] a = c.toArray();
        // If c.toArray incorrectly doesn't return Object[], copy it.
        // 如果转换后的数组a类型不是Object数组,则转换为Object数组
        if (a.getClass() != Object[].class)
            a = Arrays. copyOf(a, a.length, Object[]. class);
        // 将数组a赋值给队列的底层数组queue
        queue = a;
        // 将队列的元素个数设置为数组a的长度
        size = a.length ;
    }

2.PriorityQueue的入队实现

(1)父节点的键值总是小于或等于任何一个子节点的键值

(2)基于数组实现的二叉堆,对于数组中任意位置的n上元素,其左孩子在[2n+1]位置上,其有孩子在[2n+2]位置上,其父类在[n/2]位置上,根节点的位置为[0];

/**
     * 添加一个元素
     */
    public boolean add(E e) {
        return offer(e);
    }
     
    /**
     * 入队
     */
    public boolean offer(E e) {
        // 如果元素e为空,则排除空指针异常
        if (e == null)
            throw new NullPointerException();
        // 修改版本+1
        modCount++;
        // 记录当前队列中元素的个数
        int i = size ;
        // 如果当前元素个数大于等于队列底层数组的长度,则进行扩容
        if (i >= queue .length)
            grow(i + 1);
        // 元素个数+1
        size = i + 1;
        // 如果队列中没有元素,则将元素e直接添加至根(数组小标0的位置)
        if (i == 0)
            queue[0] = e;
        // 否则调用siftUp方法,将元素添加到尾部,进行上移判断
        else
            siftUp(i, e);
        return true;
    }

入队时siftUp方法:

/**
     * 向上移动,x表示新插入元素,k表示新插入元素在数组的位置
     */
    private void siftUp(int k, E x) {
        // 如果比较器comparator不为空,则调用siftUpUsingComparator方法进行上移操作
        if (comparator != null)
            siftUpUsingComparator(k, x);
        // 如果比较器comparator为空,则调用siftUpComparable方法进行上移操作
        else
            siftUpComparable(k, x);
    }

    private void siftUpComparable(int k, E x) {
        // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
        Comparable<? super E> key = (Comparable<? super E>) x;
        // k>0表示判断k不是根的情况下,也就是元素x有父节点
        while (k > 0) {
            // 计算元素x的父节点位置[(n-1)/2]
            int parent = (k - 1) >>> 1;
            // 取出x的父亲e
            Object e = queue[parent];
            // 如果新增的元素k比其父亲e大,则不需要"上移",跳出循环结束
            if (key.compareTo((E) e) >= 0)
                break;
            // x比父亲小,则需要进行"上移"
            // 交换元素x和父亲e的位置
            queue[k] = e;
            // 将新插入元素的位置k指向父亲的位置,进行下一层循环
            k = parent;
        }
        // 找到新增元素x的合适位置k之后进行赋值
        queue[k] = key;
    }

    private void siftUpUsingComparator(int k, E x) {
        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;
        }
        queue[k] = x;
    }

向上移动,就是不断将新增的元素和其父亲元素进行大小比较,比其父亲小则上移,最终找到合适位置;

3.PriorityQueue出队实现:

出队操作,是要删除根元素,也就是最小的元素,要删除根元素,就要找一个代替者移动到跟位置,想对于被删除的元素来说。就是向下移动;

/**
     * 删除并返回队头的元素,如果队列为空则抛出NoSuchElementException异常(该方法在AbstractQueue中)
     */
    public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

    /**
     * 删除并返回队头的元素,如果队列为空则返回null
     */
   public E poll() {
        // 队列为空,返回null
        if (size == 0)
            return null;
        // 队列元素个数-1
        int s = --size ;
        // 修改版本+1
        modCount++;
        // 队头的元素
        E result = (E) queue[0];
        // 队尾的元素
        E x = (E) queue[s];
        // 先将队尾赋值为null
        queue[s] = null;
        // 如果队列中不止队尾一个元素,则调用siftDown方法进行"下移"操作
        if (s != 0)
            siftDown(0, x);
        return result;
    }

向下移动源代码:


    /**
     * 下移,x表示队尾的元素,k表示被删除元素在数组的位置
     */
    private void siftDown(int k, E x) {
        // 如果比较器comparator不为空,则调用siftDownUsingComparator方法进行下移操作
        if (comparator != null)
            siftDownUsingComparator(k, x);
        // 比较器comparator为空,则调用siftDownComparable方法进行下移操作
        else
            siftDownComparable(k, x);
    }

    private void siftDownComparable(int k, E x) {
        // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
        Comparable<? super E> key = (Comparable<? super E>)x;
        // 通过size/2找到一个没有叶子节点的元素
        int half = size >>> 1;     
        // 比较位置k和half,如果k小于half,则k位置的元素就不是叶子节点
        while (k < half) {
             // 找到根元素的左孩子的位置[2n+1]
            int child = (k << 1) + 1; 
             // 左孩子的元素
            Object c = queue[child];
             // 找到根元素的右孩子的位置[2(n+1)]
            int right = child + 1;
            // 如果左孩子大于右孩子,则将c复制为右孩子的值,这里也就是找出左右孩子哪个最小
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue [right]) > 0)
                c = queue[child = right];
            // 如果队尾元素比根元素孩子都要小,则不需"下移",结束
            if (key.compareTo((E) c) <= 0)
                break; 
            // 队尾元素比根元素孩子都大,则需要"下移"
            // 交换跟元素和孩子c的位置
            queue[k] = c;
            // 将根元素位置k指向最小孩子的位置,进入下层循环
            k = child;
        }
        // 找到队尾元素x的合适位置k之后进行赋值
        queue[k] = key;
    }

    private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        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 = queue[child = right];
            if (comparator .compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }


  PriorityQueue在删除过程中,不是直接将根元素删除,然后再将下面的元素做上移,重新补充根元素;而是找出队尾的元素,并在队尾的位置上删除,然后通过根元素的下移,给队尾元素找到一个合适的位置,最终覆盖掉跟元素,从而达到删除根元素的目的。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值