队列的简介
- 队列(Queue)是一个有序的列表,可以通过数组和链表的方式实现,队列都是遵循先进先出的原则,即FIFO。
- 在JDK中的ArrayBlockingQueue类的底层实现就是通过数组来实现有序队列。
数组实现队列的流程
队列的本身是有序的列表,如果使用数组来存储队列数据,则队列声明入下图:
putIndex用于标记元素插入的索引,默认初始化为0;
takeIndex用于标记元素被读取的索引,默认初始化为0;
我们需要将这个数组做成可以循环使用的容器。例如如下我们添加了4个元素,如下图:
这时候putIndex就=4,因为添加完以后,索引需要指向下一个元素的索引。但是这时候读取的索引依旧=0,没有变化,我们如下图,取出第一位:
这时候takeIndex=1,也是因为弹出之后,需要指向下一个弹出的元素索引。
这时候似乎还是看不出"循环使用"这个词的效果。不要着急,下图所示:
这是时我们再添加三个数据。这时候如果putIndex一直+1的话可能就会超过这个容器的长度,所以在添加的时候,我们需要做边界值的判断。如果超过容器长度,且容器未满,需要再将索引重置到0,从头部再依次添加。这样就达到了容器循环使用的效果。若容器已满,则不允许从头部再添加,否则会出现数据覆盖,此时应当给出提示,容器已满。
代码演示
以下是一个排队取号的小程序做了一个demo,仅供参考。
不足的地方还请各位指出。
下一篇将介绍链表实现队列。
public class ArrayQueue {
/***
* 存储数据
*/
private Integer[] array;
/***
* 标记数组被读取的索引
*/
private int takeIndex;
/***
* 标记数组添加后的索引
*/
private int putIndex;
/***
* 队列中元素的个数
*/
private int count;
private ReentrantLock reentrantLock;
public ArrayQueue(int initSize) {
// 初始化数组容器
array = new Integer[initSize];
reentrantLock = new ReentrantLock();
}
/***
* 添加数据
* @param value
*/
public void put(int value) {
reentrantLock.lock();
try {
if (array.length == count) {
System.out.println("队列已满...");
putIndex = 0;
return;
}
array[putIndex] = value;
count++;
if (++putIndex == array.length) {
putIndex = 0;
}
}finally {
reentrantLock.unlock();
}
}
/***
* 弹出数据
* @return
*/
public Integer poll() {
reentrantLock.lock();
try {
if (count == 0) {
System.out.println("队列为空...");
return null;
}
Integer value = array[takeIndex];
array[takeIndex] = null;
count--;
if (++takeIndex == array.length) {
takeIndex = 0;
}
return value;
}finally {
reentrantLock.unlock();
}
}
/***
* 查看队列头部数据
* @return
*/
public Integer peek() {
reentrantLock.lock();
try {
if (count == 0) {
System.out.println("队列为空...");
return null;
}
return array[takeIndex];
}finally {
reentrantLock.unlock();
}
}
/***
* 转为数组
* @return
*/
public Integer[] toArray() {
reentrantLock.lock();
try {
if (count == 0) {
System.out.println("队列为空...");
return new Integer[0];
}
// 重新申请容器,将有效元素都拷贝到新的容器中返回
Integer[] newArray = new Integer[count];
// 获取takeIndex后元素个数
int afterTakeIndexElement = array.length - takeIndex;
// 判断有效的元素是否都在takeIndex索引之后, 有可能考虑到新添加的元素在takeIndex之前
if (count <= afterTakeIndexElement) {
// 数组拷贝
System.arraycopy(array, takeIndex, newArray, 0, count);
} else {
// 此时的场景就是有效元素添加到了takeIndex之前,需要进行两次拷贝
// 第一次拷贝takeIndex之后的有效元素
System.arraycopy(array, takeIndex, newArray, 0, afterTakeIndexElement);
// 第二次拷贝takeIndex之前的有效元素
System.arraycopy(array, 0, newArray, afterTakeIndexElement, count - afterTakeIndexElement);
}
return newArray;
}finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(10);
String tip = "取号请按[1]; 查看下一位号请按[2]; 查看当前总共排队号请按[3]; 检票请按[4]";
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println(tip);
int inputValue = scanner.nextInt();
switch (inputValue) {
case 1: {
arrayQueue.put(new Random().nextInt());
break;
}
case 2: {
System.out.println("下一位是: " + arrayQueue.peek() + "号");
break;
}
case 3: {
System.out.println("当前一共有如下排队: " + Arrays.toString(arrayQueue.toArray()));
break;
}
default: {
System.out.println("欢迎[" + arrayQueue.poll() + "]号");
break;
}
}
}
}
}