JDK源码走读(2):容器之PriorityQueue

本文详细介绍了Java中的PriorityQueue实现原理,包括内部数据结构、构造函数、堆操作等,并探讨了如何处理不同类型的元素比较。

Java的容器分成四个系列:Set, List, Queue,Map,除Map外,其余三个都实现了Collection接口,List和Queue实现顺序存储,Map实现了K-V对。

本章分析其中实现较为简单的Queue系列

一、类实现/继承体系结构

为了对整个Queue实现/继承体系有个全貌,先将体系结构图画出来:

二、关键数据成员

(1)存储结构

PriorityQueue内部使用平衡二叉树(小根堆)保存元素,底层采用的数据结构是数组:

transientObject[] queue;

利用数组保存二叉树,那么某个节点queue[n]的左子树和右子树分别是queue[2*n+1] 和queue[2*(n+1)]。

队列queue的默认初始化大小(DEFAULT_INITIAL_CAPACITY)是11。

(2)比较运算符

优先级队列要么根据比较运算符进行排序,当运算符为null时,则根据元素的自然顺序进行排序,最小值是queue[0]。

在PriorityQueue中,有一个成员变量用于记录采取的比较运算符:

private final Comparator<? super E> comparator;

PriorityQueue要求外部传入的Comparator比较器的类型必须是当前正在构造类的本身或者是其父类、祖父类。。。总之,在继承层次上,大于等于它自身

为什么这么要求呢?

我琢磨着原因应该是这样:

一个类的比较,最终要落到对其数据成员的操作,得到比较结果,子类是继承了父类的数据成员(也许是部分,除掉一些在父类中的private成员,后面再讨论),也就是说,利用父类的Comparator进行比较时,对子类也是适用的,但反过来,子类可能定义了一些新的数据成员,是父类中没有的,如果子类的Comparator在compare函数中使用这些新的数据成员比较,那么对父类是不适用的,所以,子类可以使用父类的Comparator,反过来不行。

对于父类使用私有数据成员实现compare函数,分两种情况:

一种子类可以通过构造函数,对父类的私有成员赋值,如果构造函数中有对私有成员初始化的参数,那么这种情况也会子类的比较起作用;

另一种情况,子类本身无法使用父类的私有数据成员,又没有办法改变它,那么父类的comparator对子类的排序不起作用。

(3)修改次数

transientint modCount = 0;

数据成员modCount用于记录queue队列被修改的次数

(4)

三、构造函数

PriorityQueue提供了多种形式的构造函数,在此不一一列举,就参数来说,主要包括,队列(queue)初始化大小(initialCapacity)值、比较运算符、其他集合(包括PriorityQueue或者别的类型的集合,比如Set)

这里要讲下以下几个构造函数:

public MyPriorityQueue(Collection<?extends E> c)

       <? extends E>表示c中的元素(?)只能是类型E的子类,这样其实将一个子类对象赋值给了一个父类对象,父类对象指向了一个子类对象,即多态,比如:

     class Fruit {

     }

     class Apple extends Fruit {

     }

     List<Apple>appleL= newArrayList<Apple>();

    appleL.add(new Apple("red"));

    appleL.add(new Apple("green"));     

Queue<Fruit>fruitQ= newPriorityQueue<Fruit>(appleL);

反过来就会出错了,如果用一个存放Fruit的List作为PriorityQueue的构造函数的参数就会出错了。

注意,上面是简化的写法,代码是无法编译通过的,因为PriorityQueue在存入对象时涉及到对象之间的比较,那就要去存入的对象之间可以进行比较,一种是传递了Comparator,实现对象的比较;另一种是类实现Comparable接口的比较函数compareTo,有些类本身支持比较,比如String,Integer,但对于自定义的类,那就要求实现者自己实现compareTo,当调用PriorityQueue构造函数时没有传递Comparator,PriorityQueue在比较元素的时候就调用类的compareTo进行比较,如果二者都没有提供,那就会抛异常了。下面是实现了compareTo的例子,如:

class Fruit implements Comparable<Fruit> {

    protected String name;

    public Fruit(String name) {

        this.name = name;

    }

    public String getName() {

        returnname;

    }
   @Override
    publicint compareTo(Fruit o) {
        returnthis.name.compareTo(o.getName());
    }
}

class Apple extends Fruit {
    public Apple(String name) {
        super(name);
    }
}

四、堆操作

PriorityQueue底层使用堆进行存储,后面要讲的队列的增删改查很多时候涉及到对堆的操作。堆的操作主要包括两个:

siftUp

siftDown

PriorityQueue实现了标准的heap上浮(sift up)和下沉(sift down)操作,只是在进行元素比较时,不是直接比较元素大小,而是使用用户传入的comparator或者元素所属类的compareTo成员函数来比较大小。

   private voidsiftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, 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;
    }

    @SuppressWarnings("unchecked")

    private void siftUpUsingComparator(int k, Ex) {

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

    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, 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;
    }

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

    }

五、增

在往PriorityQueue中添加元素(add/offer)PriorityQueue在队列满的时候,自动实现扩容。扩容的操作是privatevoid grow(int minCapacity)实现的,

    private void grow(int minCapacity) {

        int oldCapacity = queue.length;

        // Double size if small; else grow by50%

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

}

add的实现就是调用offer,首先检查参数是否合法,PriorityQueue中不允许保存null元素;接下来检查队列是否已满,如果满了,执行grow,扩容;然后将元素放入队列数组,执行堆操作siftUp,当数组中不止一个元素时。

六、删

boolean remove(Object o):删除对象o,首先利用indexOf(Object o)找到o的index,然后调用removeAt(private E removeAt(int i)):

   privateE removeAt(int i) {

        // assert i >= 0 && i <size;

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

       如果不是最后一个对象,删除对象后,需要执行siftDown/siftUp操作。

       同样的,poll操作是删除堆的根元素,即队列的queue[0]元素,

public Epoll() {

        if (size == 0)

            return null;

        int s = --size;

        modCount++;

        E result = (E) queue[0];

        E x = (E) queue[s];

        queue[s] = null;

//如果s==0,则说明此次删除了队列里的最后一个元素,

//删除后,队列为空,无需执行siftDown

        if (s != 0)  

            siftDown(0, x);

        return result;

    }

七、查

peek():返回队列queue中index=0的元素,queue[0];如果为空,则返回null。
contains(Object o):
public booleancontains(Object o) {
    return indexOf(o) != -1;
}

总结:

(1)   PriorityQueue实现可以作为学习堆数据结构的范例;

(2)   PriorityQueue还有一个内部类private finalclass Itr,实现了迭代器;

源码来自:https://pan.quark.cn/s/41b9d28f0d6d 在信息技术领域中,jQuery作为一个广受欢迎的JavaScript框架,显著简化了诸多操作,包括对HTML文档的遍历、事件的管理、动画的设计以及Ajax通信等。 本篇文档将深入阐释如何运用jQuery达成一个图片自动播放的功能,这种效果常用于网站的轮播展示或幻灯片演示,有助于优化用户与页面的互动,使网页呈现更加动态的视觉体验。 为了有效实施这一功能,首先需掌握jQuery的核心操作。 通过$符号作为接口,jQuery能够迅速选取DOM组件,例如$("#id")用于选取具有特定ID的元素,而$(".class")则能选取所有应用了某类class的元素。 在选定元素之后,可以执行多种行为,诸如事件监听、样式的变更、内容的更新以及动画的制作等。 关于“一个基于jQuery的图片自动播放功能”,首要任务是准备一组图片素材,这些素材将被整合至一个容器元素之中。 例如,可以构建一个div元素,将其宽度设定为单张图片的尺寸,再借助CSS实现溢出内容的隐藏,从而构建出水平滚动的初始框架。 ```html<div id="slider"> <img src="image1.jpg" alt="Image 1"> <img src="image2.jpg" alt="Image 2"> <!-- 更多图片内容... --></div>```接着,需要编写jQuery脚本以实现图片的自动切换。 这通常涉及到定时器的运用,以设定周期性间隔自动更换当前显示的图片。 通过使用`.fadeOut()`和`.fadeIn()`方法,能够实现图片间的平滑过渡,增强视觉效果。 ```javascript$(document).re...
根据原作 https://pan.quark.cn/s/eb05e63067ef 的源码改编 Vulkan在现代图形编程领域被视为一种高效且低层级的API,它赋予开发者对硬件资源的直接支配权,从而在游戏开发、专业级渲染以及计算密集型应用中达成卓越的性能表现。 MoonVulkan作为一个针对Vulkan API的Lua语言绑定库,显著简化了使用Lua来编写Vulkan图形程序的过程。 Lua作为一种轻量级脚本语言,因其语法精炼且易于集成,经常被应用于游戏开发以及其他需要快速构建原型设计的场景。 MoonVulkan的核心宗旨是将Vulkan的强大功能引入Lua编程环境,使得开发者能够借助Lua的简洁语法来处理复杂的图形作业。 这个库提供了一套完整的Vulkan函数和结构体的Lua接口,使得Lua程序员可以直接运用Vulkan API的各类功能,例如构建设备、管理交换链、提交命令缓冲区等操作。 在MoonVulkan框架内,开发者可以运用Lua的动态属性,例如在执行期间检查错误、执行条件性判断以及循环操作,这些在C++或C语言中通常需要编写大量的辅助代码才能完成。 再者,得益于Lua的脚本特性,MoonVulkan支持热更新机制,即在程序运行过程中可以调整图形逻辑,无需对整个项目进行重新编译。 在运用MoonVulkan时,有几个关键性概念需要掌握:1. **Vulkan的初始化设置**:必须配置Vulkan实例,这是所有Vulkan操作的基础环节。 这包含选取物理设备(代表GPU)、构建一个恰当的实例配置,并妥善处理所需的扩展和层。 2. **表面的构建**:在桌面平台环境下,这通常涉及与窗口系统的互动,以获取一个象征屏幕输出的VkSurfaceKHR对象。 3. **图形与计算队列的选择...
基于STM32 F4的永磁同步电机无位置传感器控制策略研究内容概要:本文围绕基于STM32 F4的永磁同步电机(PMSM)无位置传感器控制策略展开研究,重点探讨了在缺乏机械位置传感器条件下,如何通过算法实现对电机转子位置与速度的精确估算,从而实现高性能的电机控制。文中可能涉及如滑模观测器、扩展卡尔曼滤波、高频注入法或反电动势法等核心观测技术,并结合STM32 F4高性能处理器的硬件平台进行算法实现与实验验证,旨在提升系统可靠性与降低成本。此外,研究还可能涵盖磁场定向控制(FOC)框架下的电流环、速度环控制策略设计与优化。; 适合人群:具备一定电机控制理论基础和嵌入式开发经验,从事电机驱动、电力电子或自动化相关方向的研究生、工程师及科研人员。; 使用场景及目标:①用于永磁同步电机驱动系统中替代传统位置传感器,提高系统鲁棒性和集成度;②适用于对体积、成本和可靠性要求较高的工业控制、新能源汽车、无人机、家电等领域;③目标是掌握无感控制的核心算法原理与工程实现方法,完成从理论到实际系统的搭建与调试。; 阅读建议:建议结合电机控制基础知识与STM32开发环境进行学习,重点关注观测器设计与参数整定部分,配合仿真模型与实物代码进行验证,深入理解算法在实际运行中的动态响应与抗干扰能力。
【鲁棒优化、大M法、C&CG算法】计及风、光、负荷不确定性两阶段鲁棒优化(Matlab代码实现)内容概要:本文介绍了基于Matlab代码实现的计及风、光、负荷不确定性的两阶段鲁棒优化方法,重点运用鲁棒优化理论、大M法和C&CG算法解决电力系统中的不确定性问题。文中详细阐述了模型构建过程,包括不确定集合的设定、两阶段决策机制的设计以及求解算法的实现步骤,旨在提高电力系统在可再生能源接入背景下的调度鲁棒性和经济性。同时,文档列举了大量相关Matlab仿真资源,涵盖智能优化算法、机器学习、电力系统优化、路径规划等多个方向,提供了丰富的科研技术支持与案例复现资源。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事能源系统优化工作的工程技术人员。; 使用场景及目标:①研究新能源接入背景下电力系统的优化调度问题;②掌握鲁棒优化建模方法及C&CG算法的实现技巧;③进行学术论文复现或科研项目开发;④学习Matlab在电力系统仿真中的实际应用。; 阅读建议:建议读者结合提供的网盘资源下载完整代码进行实践操作,重点关注两阶段鲁棒模型的构建逻辑与算法迭代过程,同时可参照文中提及的YALMIP工具包使用方法,提升建模效率。对于初学者,宜先熟悉基本优化理论再深入算法实现细节。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值