java容器类4:Queue深入解读
Collection的其它两大分支:List和Set在前面已近分析过,这篇来分析一下Queue的底层实现。
前三篇关于Java容器类的文章:
java容器类1:Collection,List,ArrayList,LinkedList深入解读
java容器类2:Map及HashMap深入解读
java容器类3:set/HastSet/MapSet深入解读
Queue
public interface Queue<E> extends Collection<E> {
boolean add(E var1); boolean offer(E var1); E remove(); E poll(); E element(); E peek();
}
这就是Queue接口的代码,相比于List或者Set简洁明了很多。下面介绍一下它里面接口的含义:
在处理元素前用于保存元素的 collection。除了基本的 Collection
操作外,队列还提供其他的
插入、提取和检查
操作。每个方法都存在两种形式:
一种抛出异常(操作失败时),另一种返回一个特殊值(null 或 false,具体取决于操作)。
插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。
AbstractQueue
AbstractQueue中实现了queue和Collection中部分函数,比较简单,源码如下:
public abstract class AbstractQueue<E> extends AbstractCollection<E>
implements Queue<E> {
protected AbstractQueue() {
}
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
} public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
} public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
} public void clear() {
while (poll() != null)
;
} public boolean addAll(Collection<? extends E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
从上面的调用关系可以看出来,Queue的解释中哪些是会抛出异常的调用,哪些是不会抛出异常的调用接口。
Deque
一个线性 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。(java 1.6版本中的家扣,1.8中接口有变动,但是大概含义相似)
在Java容器类1中介绍了LinkedList,链表类其实实现了 Deque的接口,所以链表支持从头部和尾部添加和移除元素。
ArrayDeque
因为链表的存储结构可能比较简单,这里介绍一下ArrayDeque,它的里面存储元素使用一个数组。 作为一个双端队列的数组,涉及到扩容和元素的拷贝的逻辑可能比较复杂些。
看一下里面的几个构造函数:
public ArrayDeque() {
elements =
new Object[16];
} public ArrayDeque(int numElements) {
allocateElements(numElements);
} public ArrayDeque(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}
从构造函数可以看出默认的会分配一个长度为16的数组。同时,也支持指定大小的队列(这里的allocateElements函数之前在
java容器类2:Map及HashMap深入解读 中已经深入分析过,是个非常精妙的函数)。下面看一下到底是如何实现插入?又是如何自动扩充数组的?
ArrayQueue中维护了两个成员变量:head和tail分别代表 队列的头和尾在数组中的下标。
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
* arbitrary number equal to tail if the deque is empty.
*/
transient int head; /**
* The index at which the next element would be added to the tail
* of the deque (via addLast(E), add(E), or push(E)).
*/
transient int tail;
在队列的首部添加元素:
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
public void
addLast(E e) {
if
(e ==
null
)
throw new
NullPointerException();
elements[tail] = e;
if
( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
由构造函数和数组分配的函数可以知道,数组的长度肯定是一个2的幂次方的一个整数。
当head为大于0的整数时,在头部插入很简单,将head前一个元素赋值为e就可以了。那么当head为0时,怎么计算的?由上面可以看出会插入到数组的尾部。所以ArrayDeque相当于在一个圆环上,规定一个头一个尾作为队列的前后(将数组的首位相连)。
在最后位置添加元素的原理和在首部添加相似。注意判断是否已满的 判断,这里不再分析。
当队列已经满后,会将数组的长度double。由于数组是不能自由扩张的,所以doubleCapacity函数应该是分配一个更大的数组,并将原来的元素拷贝进去,这里不再分析。
总的来说双端队列ArrayDeque是在数组的基础之上实现,原理和实现都不算复杂,但是很多边界调节等细节可以斟酌。
BlockingQueue
BlockingQueue是concurrent包下面的,后续打算写一个系列文章专门分析concurrent包下面的类,及一些多线程相关的东西。
PriorityQueue
优先级队列是一个可以排序的队列。内部是一个最大堆,大部分人应该了解堆排序,所以对最大堆应该不会陌生。
每次读取元素都是读取最大的元素(默认情况下)。
对外的接口有如下:
方法名 | 功能描述 |
---|---|
add(E e) | 添加元素 |
clear() | 清空 |
contains(Object o) | 检查是否包含当前参数元素 |
offer(E e) | 添加元素 |
peek() | 读取元素,(不删除) |
poll() | 取出元素,(删除) |
remove(Object o) | 删除指定元素 |
size() | 返回长度 |
PriorityQueue 默认是一个最大堆结构,如果想构造一个最小堆:
private static final int DEFAULT_INITIAL_CAPACITY = 11;
PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(DEFAULT_INITIAL_CAPACITY, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
关于堆的数据结构部分这里不再分析可以参考:https://www.cnblogs.com/tstd/p/5125949.html
c++版的优先级队列分析:优先级队列用法详解(priority_queue)
由于是通过数组保存数据,所以优先级队列也会涉及到容量的扩充等,和HashMap/Setting/Collection的扩容原理相同,甚至更简单,不再分析。PriorityQueue内部的操作都是在最大堆的基础上展开的,阅读堆的数据结构相关资料便可了解。
参考:
http://tool.oschina.net/uploads/apidocs/jdk-zh/java/util/Deque.html
http://www.cnblogs.com/NeilZhang/p/5650226.html
java容器类4:Queue深入解读的更多相关文章
- java容器类3:set/HastSet/MapSet深入解读
介绍 Set:集合,是一个不包含重复数据的集合.(A collection that contains no duplicate elements. ) set中最多包含一个null元素,否者包含了两 ...
- Java容器类接口的选择
我们知道Java容器类实际提供了四类接口:Map,List,Set和Queue,如下图所示,每种接口都有不止一个版本的实现,如果在实际编写程序时需要使用某种接口时该如何选择. 从Oracle的Java ...
- Java容器类概述
1.简介 容器是一种在一个单元里处理一组复杂元素的对象.使用集合框架理论上能够减少编程工作量,提高程序的速度和质量,毕竟类库帮我们实现的集合在一定程度上时最优的.在Java中通过java.util为用 ...
- java 容器类大集结
这个世界是程序员的世界,归根到底是数据的世界,要统治这个世界,首先要学会征服数据. 没有最好的,只有最合适的,如何在不同的环境先选择最优的存储的结构呢?且看下文分解: 以下内容部分来自网络,参考: h ...
- 【转】java 容器类使用 Collection,Map,HashMap,hashTable,TreeMap,List,Vector,ArrayList的区别
原文网址:http://www.360doc.com/content/15/0427/22/1709014_466468021.shtml java 容器类使用 Collection,Map,Hash ...
- Java容器类List、ArrayList、Vector及map、HashTable、HashMap的区别与用法
Java容器类List.ArrayList.Vector及map.HashTable.HashMap的区别与用法 ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数 ...
- java容器类---概述
1.容器类关系图 虚线框表示接口. 实线框表示实体类. 粗线框表示最经常使用的实体类. 点线的箭头表示实现了这个接口. 实线箭头表示类能够制造箭头所指的那个类的对象. Java集合工具包位于Java. ...
- Java Collection之Queue具体解释及用途
Queue是一种常见的数据结构,其主要特征在于FIFO(先进先出),Java中的Queue是这样定义的: public interface Queue<E> extends Collect ...
- Java容器类Collection,List,Set,Map.,Iterator,Collections工具类,Arrays工具类,Comparable
Java容器类Collection,List,Set,Map.,Iterator,Collections工具类,Arrays工具类,Comparable接口,泛型 Collection,List,Se ...
随机推荐
- java 集合类Array、List、Map区别和优缺点
Java集合类主要分为以下三类: 第一类:Array.Arrays 第二类:Collection :List.Set第三类:Map :HashMap.HashTable 一.Array , Array ...
- Http协议规范及格式
HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP协议的详细内容请参考RFC2616.HTTP协议采用了请求/响应模型.客户 ...
- 网络基础tcp/ip协议二
物理层解析: 物理层是TCP/IP模型的最低层,物理层为数据提供数据传输可靠通路. 物理层关心的介质: 信号 接口 传输介质 信号的分类:(信号是信息传递的媒介.) 模拟信号:连续变化的物理量 数字信 ...
- 【linux】linux下vi命令的使用
进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi +n filename :打开文件,并将光标置于第n行首 vi + filename :打开文件,并将光标置于最后 ...
- Flex弹出窗口请求Action函数
Flex弹出窗口请求Action函数 private function askQuestion(event:MouseEvent):void { var askQuestions:AskWindow ...
- 个性化WinPE封装方法 ----最后实战“制作WinPE3.0图文教程”
经过前几讲,主要目的就是准备一些"原材料",熟悉一些"命令",实际上是"战前演练准备".下面要进入"实战状态",成败在此 ...
- SCADA系统
简介编辑 在电力系统中,SCADA系统应用最为广泛,技术发展也最为成熟.它在远动系统中占重要地位,可以对现场 SCADA系统 的运行设备进行监视和控制,以实现数据采集.设备控制.测量.参数调节以及各类 ...
- Java开发速度神器Lombok,Eclipse端安装使用教程
一.Lombok简介 Lombok是一个代码生成器,可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法. 使用 ...
- 实例 centos自动挂载、备份windows共享文件夹,并删除第7日前当天的备份
此为用户twk的备份执行方案(192.168.42.246虚拟机数据库,备份到192.168.42.147的第二硬盘)此为用户twk的备份执行方案(192.168.42.5虚拟机数据库,备份到192. ...
- Gearman研习笔记(1) ------ 官网介绍要点摘录
之前的项目里使用过消息中间件(公司提供的MQ服务)来做分发,因为MQ是基于消息的,并不是专业的任务分发器,在一些复杂场景上使用起来并不恰当. 后来听组长说了下Gearman(听名字还以为是Ironma ...