Java 队列实现原理
“队列”这个单词是英国人说的“排”。在英国“排队”的意思就是站到一排当中去。计算机科学中,队列是一种数据结构,有点类似栈,只是在队列中第一个插入的数据项也会最先被移除,而在栈中,最后插入的数据项最先移除。队列的作用就像电影院前的人们站成的排一样:第一个进入附属的人将最先到达队头买票。最后排队的人最后才能买到票。
队列和栈一样也被用作程序员的工具。它也可以用于模拟真实世界的环境,例如模拟人们在银行里排队等待,飞机等待起飞,或者因特网络上数据包等待传送。
在计算机操作系统里,有各种队列在安静地工作着。打印作业在打印队列中等待打印。当在键盘上敲击时,也有一个存储键入内容的队列。同样,如果使用文字处理程序敲击一个键,而计算机又暂时要做其它的事,敲击的内容不会丢失,它会排在队列中等待,直到文字处理程序有时间来读取它。利用队列保证了键入内容在处理时其顺序不会改变。
队列的基本操作
队列的两个基本操作是inserting(插入)一个数据项,即把一个数据项放入队尾,另一个是removing(移除)一个数据项,即移除队头的数据项。这类似于电影爱好者排队买票时先排到队尾,然后到达队头买票后离开队列。
栈中的插入和移除数据项方法的命名是很标准,称为push和pop。队列的方法至今没有标准化的命名。“插入”可以称为put、add或 enque,而“删除”可以叫delete、get或deque。插入数据项的队尾,也可以叫作back、tail或end。而移除数据项的队头,也可以叫head。下面将使用insert、remove、front和rear。
插入将值插入队尾,同时队尾箭头增加一,指向新的数据项。
数据项被移除后,同时队头指针增加一。通常实现队列时,删除的数据项还会保存在内存中,只是它不能被访问了,因为队头指针已经移到它的下一个位置了。
和栈中的情况不同,队列中的数据项不总是从数组的0下标处开始。移除了一些数据项后,队头指针会指向一个较高的下标位置。
查看操作返回队头数据项的值,然而并不从队中删除这个数据项。
要是想从空队列中移除一个数据项或想在已经满的队列中插入一个数据项,应用程序都要提示出错消息。
循环队列
当在队列中插入一个新数据项,队头的Rear箭头向上移动,移向数组下标大的位置。移除数据项时,队尾Front指针也会向上移动。这种设计可能和人们直观察觉相反,因为人们在买电影票排队时,队伍总是向前移动的,当前面的人买完票离开队伍后,其他人都向前移动。计算机中在队列里删除一个数据项后,也可以将其他数据项都向前移动,但这样做的效率很差。相反,我们通过队列中队头和队尾指针的移动保持所有数据项的位置不变。
这样设计的问题是队尾指针很快就会移到数组的末端。虽然在数组的开始部分有空的数据项单元,这是移除的数据项的位置,但是由于因为队尾指针不能再向后移动了,因此也不能再插入新的数据项,这该怎么办?
环绕式处理
为了避免队列不满却不能插入新数据项的问题,可以让队头队尾指针绕回到数组开始的位置。这就是循环队列(有时也称为“缓冲环”)。
指针回绕的过程:在队列中插入足够多的数据项,使队尾指针指向数组的未端。再删除几个数组前端的数据项。现在插入一个新的数据项。就会看到队尾指针从未端回绕到开始处的位置。新的数据项将插入这个位置。
插入更多的数据项。队尾指针如预计的那样向上移动。注意在队尾指针回绕之后, 它现在处在队头指针的下面,这就颠倒了初始的位置。这可以称为“折断的序列”:队列中的数据项存在数组两个不同的序列中。
删除足够多的数据项后,队头指针也回绕。这时队列的指针回到了初始运行时的位置状态,队头指针在队尾指针的下面。数据项也恢复为单一的连续的序列。
队列的Java代码
Queue.java程序创建了一个Queue类,它有insert()、remove()、peek()、isEmpty()和size()方法。
package 栈和队列;
class Queue{ private int maxSize; private long[] queArray; private int front; private int rear; private int nItems; public Queue(int s){ maxSize=s; queArray=new long[maxSize]; front=0; rear=-1; nItems=0; } public void insert(long j){ if(rear==maxSize-1) rear=-1; queArray[++rear]=j; nItems++; } public long remove(){ long temp=queArray[front++]; if(front==maxSize) front=0; nItems--; return temp; } public long peekFront(){ return queArray[front]; } public boolean isEmpty(){ return (nItems==0); } public boolean ifFull(){ return (nItems==maxSize); } public int size(){ return nItems; } }
程序实现的Queue类中不但有front(队头)和rear(队尾)字段,还有队列中当前数据项的个数:nItems。
Insert()方法运行的前提条件是队列不满。在Main()中没有显示这个方法,不过通常应该先调用isFull()方法并且返回false 后,才调用insert()方法。(更通用的做法是在insert()方法中加入检查队列是否满的判定,如果出现向已满队列里插入数据项的情况就抛出异常。)
一般情况,插入操作是rear(队尾指针)加一后,在队尾指针所指的位置处插入新的数据。但是,当rear指针指向数组的顶端,即 maxSize-1位置的时候,在插入数据项之前,它必须回绕到数组的底端。回绕操作把rear设置为-1,因此当rear加1后,它等于0,是数组底端的下标值,最后nItem加一。
Remove()方法运行的前提条件是队列不空,在调用这个方法之前应该调用isEmpty()方法确保队列不空,或者在remove()方法里加入这种出错检查的机制。
移除(remove)操作总是由front指针得到队头数据项的值,然后将front加一。但是,如果这样做使front的值超过数组的顶端,front就必须绕回到数组下标为0的位置上。作这种检验的同时,先将返回值临时存储起来。最后nItem减一。
Peek()方法简单易懂:它返回front指针所指数据项的值。有些队列的实现也允许查看队列队尾数据项的值;比如这些方法可称为peekFront()、peekRear()、或者只是front()和rear()。
isEmpty()、isFull()和size()方法的实现都依赖于nItems字段,它们分别返回nItems是否等于0,是否等于maxSize,或者返回它本身值。
在Queue类中包含数据项计数字段nItems会使insert()和remove()方法增加一点额外的操作,因为insert()和 remove()方法必须分别递增和递减这个变量值。这可能算不上额外的开销,但是如果处理大量的插入和移除操作,这就可能会影响性能了。
因为,一些队列的实现不使用数据项计数的字段,而是通过front和rear来计算出队列是否空或者满以及数据项的个数。如果这样做,isEmpty()、ifFull()和size()例程会相当复杂,因为就像前面讲过的那样,数据项的序列或者被折成两段,或者是连续的一段。
而且,一个奇怪的问题出现了。当队列满的时候,front和rear指针取一定的位置,但是当队列为空时,也可能呈现相同的位置关系。于是在同一时间,队列似乎可能是满的,也可能是空的。这个问题的解决方法是:让数组容量比队列数据项个数的最大值学要大一。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
本文向大家介绍Java Unsafe类实现原理及测试代码,包括了Java Unsafe类实现原理及测试代码的使用技巧和注意事项,需要的朋友参考一下 Unsafe类介绍 第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe?读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能。 Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带
本文向大家介绍布隆过滤器的原理以及java 简单实现,包括了布隆过滤器的原理以及java 简单实现的使用技巧和注意事项,需要的朋友参考一下 一.布隆过滤器 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困
本文向大家介绍简单了解mybatis拦截器实现原理及实例,包括了简单了解mybatis拦截器实现原理及实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了简单了解mybatis拦截器实现原理及实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 例行惯例,先看些基本概念: 1 拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加
主要内容:顺序队列简单实现,顺序队列另一种实现方法顺序队列 ,即采用顺序表模拟实现的队列结构。 我们知道,队列具有以下两个特点: 数据从队列的一端进,另一端出; 数据的入队和出队遵循"先进先出"的原则; 因此,只要使用顺序表按以上两个要求操作数据,即可实现顺序队列。首先来学习一种最简单的实现方法。 顺序队列简单实现 由于顺序队列的底层使用的是数组,因此需预先申请一块足够大的内存空间初始化顺序队列。除此之外,为了满足顺序队列中数据从队尾进,队头出且
本文向大家介绍ios实现简易队列,包括了ios实现简易队列的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了ios实现简易队列的具体代码,供大家参考,具体内容如下 满足一些特殊需求 接口部分(队列支持需求) 实现方法 测试 结果 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
本文向大家介绍链表的原理及java实现代码示例,包括了链表的原理及java实现代码示例的使用技巧和注意事项,需要的朋友参考一下 一:单向链表基本介绍 链表是一种数据结构,和数组同级。比如,Java中我们使用的ArrayList,其实现原理是数组。而LinkedList的实现原理就是链表了。链表在进行循环遍历时效率不高,但是插入和删除时优势明显。下面对单向链表做一个介绍。 单链表的概念 链表是最基本