JDK源码阅读(一)、PriorityQueue

本文详细介绍了优先队列的内部实现原理,重点讲解了小根堆的节点插入和删除过程,同时提供了JDK源码的具体实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面,最近准备春招找实习,还有项目要做,一直没更新博客。不过通过这次找实习总结出来一个结论,基础很重要。 唉,这次找实习就当试试水了,总结一下经验,打好基础。秋招再战~
JDK源码一个学长也说了,算是集大家之精华,读了有益无害哈~

一、 优先队列的内部原理

 优先队列,通过名字就应该知道它内部是按照一定的优先关系进行元素的排列。而优先队列默认内部采用小根堆维护着一个一维数组
  至于什么是堆,先说一下定义:

堆是一棵完全二叉树,并且满足一定排序关系:父亲节点比它的任意子节点大的称作大根堆,父节点比它的任意节点小的称作小根堆。从这可以知道根节点要么最小要么最大。 

 知道了什么是堆,下面就需要思考一下,当一个元素插入或者删除的时候,怎么才能让其满足小根堆(大根堆)条件,维护一个优先关系。以下拿小根堆举例子
   节点的插入:

1、将节点插入到树的最后
2、沿着插入位置逐步与父节点比较,如果比父节点小交换位置,如此继续下去,直到大于等于父节点停止

   节点的删除

1、将指定位置的节点删除,将队尾最后的叶子节点插入到删除的位置
2、如果当前删除位置子女节点不为空,则与其子女节点比较,如果大于其子女,向下交换位置,如此继续,直到小于等于其子女节点。
3、如果发现删除位置节点小于其父亲节点,向上交换位置,如此继续,知道大于等于其父节点停止。
二、源码阅读

  首先,优先队列采用一个一维数组存储元素,用这个数组表示堆(也市二叉树)。在默认情况下这个数组的大小为11。另外一个构造函数传入大小以及比较方法(用于进行元素大小比较)。

private Object[] queue;
private static final  int MAX_ARRAY_SIZE=Integer.MAX_VALUE-8;
public SimplePriorityQueue(){
        this(DEFAULT_INITIAL_CAPACITY,null);
 }
public SimplePriorityQueue(int size,Comparator<? super E> comparator) {
        if (size < 1)
            throw new IllegalArgumentException();
        this.comparator = comparator;
        this.queue=new Object[size];
}

  紧接着我们直接看元素添加操作,

    public boolean add(E e){
        return offer(e);//调用offer方法
    }
    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;

        if (i == 0){
            queue[0]=e;//第一个元素直接放在0位置,也是堆的根节点
        }
        else
            siftUp(i,e);//放在堆的最后,然后向上调整

        return true;
    }
    /**
     * 选择二叉树堆的比较策略
     * 如果设置了自定义Comparator,则使用自定义Comparator
     * 未设置使用默认比较
     * @param k
     * @param e
     */
    private void siftUp(int k, E e) {
        if (comparator != null){
            siftUpUsingComparator(k, e);
        }
        else
            siftUpComparable(k, e);

    }
    /**
     * 采用小根堆维护顺序
     *  规则:将新插入的元素放在最后,然后与它的父节点进行比较。
     *  如果比父节点小,则交换位置。这样继续下去,找到合适的位置
     * @param k
     * @param x
     */
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        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;
    }

    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;
    }

  通过这些我们就可以总结出来,在进行添加元素的时候,先判断是否超出一维数组的大小,如果超出进行扩容,否则,按照小根堆添加元素策略加入元素。
  元素删除

    //以下的代码都一样,进行元素的删除,都是调用同一个方法reomvaAt
    /**
     * 删除指定位置的元素
     *    含义:如果删除i位置的节点之后,最后面的叶子节点在经过
     *     与当前节点位置下面节点(sifeDown)排序后,发现还在i位置,表示比下面的节点小。这时候需要再判断一下
     *     是否当前节点与上面的节点需要调整,查看是否比上面的还小(sifeUp)
     *    当之前的叶子节点调整到当前位置的上一级节点后,就需要返回这个节点。
     *  @param i
     * @return
     */
    private E removeAt(int i){
        modCount++;

        int s= --size;

        if (s==i){
            queue[i]=null;
        }

        else{
            E moved= (E) queue[s];
            queue[s]=null;
            siftDown(i,moved);
            if (queue[i]==moved){
                siftUp(i,moved);
                if (queue != moved){
                    return moved;
                }
            }
        }
        return  null;
    }

    boolean removeEq(Object o){
        for (int i=0;i< size; i++){
            if (o==queue[i]){
                removeAt(i);
                return true;
            }
        }
        return false;
    }
    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;
    }

先说这么多吧,可能发现有一个modCount,每次进行添加或者删除之后都会自增,其实它是在优先队列的迭代器中使用的,防止在多线程情况下,一个线程向优先队列中添加了元素,另外一个线程这时候还在迭代之前没有添加元素旧的迭代器中迭代。当发生这种情况时候,直接抛出异常,添加还好说,如果是删除如果不采用这种方式可能就会产生数组越界的异常。

就写这么多了,如果有不对的,请大佬们指出~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值