【Java集合】Queue源码

本文详细介绍了Java集合框架中的ArrayDeque和PriorityQueue。ArrayDeque作为双端队列,利用循环数组实现高效操作,而PriorityQueue基于最小堆结构,适用于排序需求。文章探讨了它们的内部结构、扩容机制以及操作原理,并提供了相应的UML类图。ArrayDeque在扩容时通过位运算优化容量,PriorityQueue在添加元素后会进行堆化保证排序。

1. ArrayDeque

ArrayDeque,数组双端队列(具有栈的特点),实现了Deque(Queue)接口,线程不安全。

ArrayDeque内部结构是一个循环数组,它的成员变量如下:

    transient Object[] elements; 

    transient int head;

    transient int tail;

    private static final int MIN_INITIAL_CAPACITY = 8;

它的扩容操作如下(通过移位或的操作进行放大倍数,使得数组的容量值不必严格满足2的幂):

    // 初始化时分配数组大小
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // 最佳匹配元素与2的乘积
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;

            if (initialCapacity < 0)    // Too many elements, must back off
                initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }
    
    // 新增元素时扩容
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }

    private void doubleCapacity() {
        assert head == tail;
        int p = head;
        int n = elements.length;
        int r = n - p; // number of elements to the right of p
        int newCapacity = n << 1;
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        head = 0;
        tail = n;
    }

ArrayDeque的UML类图如下:

2. PriorityQueue

PriorityQueue队列使用的是最小堆结构,可用于排序。

它的成员变量与其中之一的构造方法如下:

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

    private static final int DEFAULT_INITIAL_CAPACITY = 11;
 
    /**
     * 优先队列队列[n]有两个子队列:队列[2*n+1]和队列[2*(n+1)]。
     * 优先级队列由比较器排序,如果比较器为空,则由元素的自然顺序排序:
     * 对于堆中的每个节点n和n的每个后代d,n<=d。
     */
    transient Object[] queue; 

    private int size = 0;

    private final Comparator<? super E> comparator;

    // 优先队列已被删除的次数
    transient int modCount = 0;
    
    // 构造方法
    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);
        }
    }
    
    private void initElementsFromCollection(Collection<? extends E> c) {
        Object[] a = c.toArray();
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, a.length, Object[].class);
        int len = a.length;
        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;
    }
    
    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
        if (c.getClass() == PriorityQueue.class) {
            this.queue = c.toArray();
            this.size = c.size();
        } else {
            initFromCollection(c);
        }
    }

    private void initFromCollection(Collection<? extends E> c) {
        initElementsFromCollection(c);
        heapify();
    }

}

构造方法中需要注意的是,initElementFromCollection 方法本身并不能保证初始化后queue数组元素堆化/排序,所以 initFromCollection 方法在调用完 initElementFromCollection 后进行了堆化heapify;至于传入的集合是SortedSet时,PriorityQueue会继续使用SortedSort中的Comparator对象来排序,无论怎么排序,新创建的PriorityQueue中的数据对象都会符合PriorityQueue对数据对象存储的要求。

扩容操作:

    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // 如果原始容量小于64则双倍扩容,否则扩大1.5倍
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        queue = Arrays.copyOf(queue, newCapacity);
    }

优先队列的UML图如下:

优先队列的工作原理

优先队列:堆(建堆、排序,堆是一种特殊的树),为了读写平衡,PriorityQueue默认最小堆,可以通过Comparator接口干预成最大堆。该堆中有两个主要的方法:升序操作 siftUp 、降序操作 siftDown,建堆时用siftDown,添加元素时用到了siftUp。

自行理解下移除函数吧(它同时用到了siftUp与siftDown方法)。。。

    private E removeAt(int i) {
        modCount++;
        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;
    }

从队列中删除第i个元素。通常情况下,这种方法会将元素保留i-1以内不受影响。如果恰好是最好一个元素,那么移除它返回null。为了保持堆不变,它必须将列表中较后的元素替换为i元素较前的元素,这种情况下,此方法返回以前位于列表末尾的元素、现在位于i之前的某个位置。

3. 总结

ArrayDeque采用双端队列对数组进行了充分利用,PriorityQueue堆基于权值的排序性能进行了优化。

Metasequoia 又称水杉建模,这是一款体积小巧的3D建模软件,由O.Mizno先生开发,可以用于一些游戏中的三维形状数据制作等等,并拥有强大的可移植功能,是3D建模不可多得的一款工具。Metasequoia 是适用于 Windows 或 MacOS/OS X 的 3D CG 建模软件。您可以在不同的平台上以相同的方式使用它。(*部分功能不同。)所有 GUI 组件都通过矢量图来显示。按钮和图标在高分辨率监视器(如 IGZO 或 Retina 显示器)中显示得非常漂亮。日本3D水杉建模工具 Metasequoia 4.6.5 + x64 中文汉化版Metasequoia 功能特色:强大的建模功能Metasequoia 有基本的多边形编辑功能。而且它也有帮助更好的处理的功能。在版本4.0或更高版本中添加和改进了以下功能。填满一个洞 – 它会自动填充表面上的孔。桥 – 在孔之间创建一个管状多边形。斜角 – 创建圆角曲面(圆角)被添加…等等。N-gon(脸上5点以上)你可以制作一个有5个或更多点的多边形。你可以很容易地添加边(划分一个面)或擦除边(合并面)。电枢通过[电枢],您可以沿着中心线创建物体形状。您可以通过仅指定中心线和线宽来创建对象。此功能将减少创建对象的时间。而且您可以通过建模功能更详细地编辑生成的对象。骨和Morph设置[骨骼],可以将对象链接到骨骼的变形。而[Morph]是一个滑动顶点的函数。您可以创建诸如面部表情。骨骼和Morph设置可以导出为相应的文件格式,FBX或PMD。测量(仅限于水杉4 EX)您可以测量对象的大小。此功能用于商业用途或3D打印机输出。查看单位您可以在屏幕上显示毫米或英寸等单位。而且,可以用单位进行数值的输入。测量长度,角度和厚度您可以测量任何位置的距离,角度和厚度。与对象的移动或变形相关联的数值的变化立即显示。UV编辑[UV编辑]功能大大提高。新功能;自动解包,分割或拼接等。 UV编辑更高效。OpenSubdivOpenSubdiv;一个皮克斯的细分表面;现在支持。通过将权重设置为顶点或线,生成对象的边缘。而且,你得到的纹理,其变形紫外线较少。如果将OpenSubdiv对象导出到FBX文件,则可以在兼容软件之间传输形状数据。环境遮挡(仅水杉4 EX)环境遮挡使隐蔽部分变暗,开放部分变亮。这个结果被导出到顶点颜色,你可以从任何方向检查对象。支持 RenderManRendeMan 水杉4可以获得高质量的渲染图像。(*另外,需要安装RenderMan(商业或非商业)。)(*在标准版中,渲染分辨率有一个上限。)导入/导出水杉支持第三方3DCG软件的几种文件格式。您可以使用大多数3DCG软件将文件传递给对方。当您导入或导出时,水杉将支持坐标轴的转换,反转表面方向,并指定放大系数。它将解决每个软件的差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值