zoukankan      html  css  js  c++  java
  • PriorityQueue源码解析

    PriorityQueue

    PriorityQueue基于小根堆实现,是一个无界队列,不允许null元素。底层存储使用数组,索引n的元素的左右两个孩子索引分别为2n + 12n + 2

    PriorityQueue元素通过比较器排序,如果比较器为空,则使用自然排序。

    PriorityQueue默认容量大小为11,当存储数组中总元素个数等于数组长度时,触发扩容。扩容时,如果原存储数组长度小于64,则扩容为原来的1倍,否则扩容50%,扩容中使用Arrays.copyOf复制旧数组元素到新数组。

    关键属性

    // 默认数组容量11
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    // 存储队列元素 queue[n]的左右孩子分别为queue[2n + 1]、queue[2(n+1)]
    transient Object[] queue; 
    // 优先队列中总元素个数
    private int size = 0;
    // 排序比较器
    private final Comparator<? super E> comparator;
    // 结构性修改次数
    transient int modCount = 0;
    

    构造函数

    /**
     * 使用默认容量(11)、元素之间使用自然排序
     */
    public PriorityQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }
    
    /**
     * 指定初始容量,元素之间使用自然排序
     */
    public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }
    
    /**
     * 指定初始容量,指定比较器
     */
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        // 初始存储数组,大小为initialCapacity
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
    

    新增元素

    public boolean add(E e) {
        return offer(e);
    }
    
    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        // 结构性修改次数+1
        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;
    }
    
    /**
     * 添加元素至队列,上浮
     * @param k 队列中总元素个数
     * @param x 新增元素
     */
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }
    
    /**
     * 使用自然排序排列
     * 插入元素时,先将新增元素放在最后一个元素位置,然后将新增元素与父级元素比较,如果大于等于父级元素,新增元
     * 素便插入在最后一个位置;否则循环向上查找父节点,直到新增元素不小于父节点
     * @param k 队列中总元素个数
     * @param x 新增元素
     */
    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            // 找到索引为k的节点的父节点
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            // 新增节点大于等于父节点,不需要再调整
            if (key.compareTo((E) e) >= 0)
                break;
            // 将k位置设置为父节点
            queue[k] = e;
            // 搜索节点上浮
            k = parent;
        }
        // 上浮到k
        queue[k] = key;
    }
    
    @SuppressWarnings("unchecked")
    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;
    }
    
    

    扩容

    /**
     * 扩容。新建数组,将数组扩容后,复制旧数组元素到新数组中
     * 当总元素个数等于数组长度时,触发扩容
     * 当数组长度小于64时,扩容一倍;否则扩容50%
     */
    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        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);
    }
    
    /**
     * 溢出处理
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
    }
    

    获取元素

    /**
     * 获取元素。取出根节点(数组的第一个元素)
     * 取出第一个元素后,将最后一个元素移动到index=0位置,然后与index = 1、index = 2 位置的元素比较,如果
     * 最后一个元素比两个孩子大,则找出左右孩子中较小的,作为根节点。
     */
    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];
        queue[s] = null;
        // 下沉最后一个元素
        if (s != 0)
            siftDown(0, x);
        return result;
    }
    
    /**
     * 下沉元素
     * @param k 0 
     * @param x 最后一个元素
     */
    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
    
    
    /**
     * 下沉元素
     * @param k 0 
     * @param x 最后一个元素
     */
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        // 由于index = k位置的元素的左右孩子在2n + 1 和 2(n + 1)位置,
        // 所以,如果k 大于 总元素数量的一半,那么该位置的元素也就不存在孩子节点,不需要调整
        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;
            
            // 最后一个节点比index = k 的左右节点大,那么左右节点中的较小者上浮做父节点,继续下沉最后一个节点
            
            // 较小孩子上浮
            queue[k] = c;
            // 最后一个节点继续下城 
            k = child;
        }
        // 最后一个节点下沉到k位置
        queue[k] = key;
    }
    
    @SuppressWarnings("unchecked")
    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;
    }
    

    移除元素

    /**
     * 移除指定元素
     * 如果存在指定元素,返回true,否则,返回false
     */
    public boolean remove(Object o) {
        // 获取指定元素在数组中位置
        int i = indexOf(o);
        // 不存在指定元素,返回false
        if (i == -1)
            return false;
        // 存在指定元素,移除,并返回true
        else {
            removeAt(i);
            return true;
        }
    }
    
    /**
     * 获取指定元素的在数组中位置
     */
    private int indexOf(Object o) {
        if (o != null) {
            // 遍历数组,如果存在相同元素,返回元素所在下标
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        // 不存在相同元素,返回-1
        return -1;
    }
    
    /**
     * 移除指定下标的元素
     */
    private E removeAt(int i) {
        // assert i >= 0 && i < size;
        // 结构性修改+1
        modCount++;
        // 总元素个数-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;
    }
    
  • 相关阅读:
    centos7下安装docker(17.2docker监控---weave scope)
    centos7下安装docker(17.1docker监控---sysdig)
    centos7下安装docker(17docker监控---docker自带监控命令)
    centos7下安装docker(16.1docker跨主机存储--Rex-Ray)
    centos7下安装docker(16.docker跨主机存储)
    centos7下安装docker(15.8docker跨主机容器通信总结)
    centos7下安装docker(15.7容器跨主机网络---calico)
    centos7下安装docker(15.6docker跨主机网络---Weave)
    ETSI GS MEC 013,UE 位置 API
    ETSI GS MEC 014,UE 标识 API
  • 原文地址:https://www.cnblogs.com/QullLee/p/12238266.html
Copyright © 2011-2022 走看看