基本介绍
- 队列是一个有序列表,可以用
数组或者链表来实现; - 队列遵循
先入先出的原则,即:先存入队列的数据要先取出,后存入的数据要后取出来。
实现方式
数组模拟队列
队列本身是有序列表,使用数组的结构来存储队列的数据。
实现思路
- maxSize 是该队列的最大容量;
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:
实现逻辑
入队列操作 addQueue
- 将尾指针往后移:rear+1 , 当 front == rear 时标识队列空;
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,当 rear == maxSize - 1 时标识队列满。
public boolean addQueue(int temp) throws Exception {
if (isFull()) {
throw new Exception("队列已满,不能添加元素");
}
this.rear++;
nums[rear] = temp;
return true;
}
出队列操作 pop
- 当 front == rear 时标识队列空,队列里没有元素;
- 将头指针往后移:front+1 ,获取头元素。
public int pop() throws Exception {
if (isEmpty()) {
throw new Exception("队列已空,不能获取元素");
}
this.front++;
int result = nums[front];
nums[front] = 0;
return result;
}
显示队列的情况 showQueue
public void show() {
for (int temp : nums) {
System.out.print(temp + " ");
}
System.out.println();
}
查看队列头元素 headQueue
- 当 front == rear 时标识队列空,队列里没有元素;
- 将下标定位到头指针后一位,但不移动头指针,获取头元素。
public int get() throws Exception {
if (isEmpty()) {
throw new Exception("队列已空,不能获取元素");
}
return this.nums[this.front + 1];
}
问题分析并优化
- 队列用完即弃,无法复用,可以改造成循环队列,达到循环利用的效果;
- 队列一经创建无法扩容,可以添加队列满则自动扩容函数。
数组模拟环形队列
通过将数组取模的方式来实现一个环形队列。
实现思路
- front 指向队列的第一个元素,也就是说 arr[front] 就是队列的第一个元素,front 的初始值为0;
- rear 指向队列的最后一个元素的后一个位置,因为希望空出一个空间做为约定,rear 的初始值为0;
- 当队列满时,条件是 (rear + 1) % maxSize == front;
- 对队列为空的条件 rear == front;
- 队列中有效的数据的个数 (rear + maxSize - front) % maxSize; // 比如 rear = 1、front = 0。
实现逻辑
入队列操作 addQueue
- 此时 rear 指向队列尾节点,可以直接设置;
- 防止尾指针超过数组界限,需要对 rear + 1 后对 maxSize 取模。
public void add(int temp) throws Exception {
if (isFull()) {
throw new Exception("队列已满,不能添加元素");
}
nums[rear] = temp;
rear = (rear + 1) % maxSize;
}
出队列操作 getQueue
- 此时 front 指向队列头节点,可以直接获取;
- 需要将 front 指向下一个节点,同样为了避免数组越界,对 front + 1 后对 maxSize 取模。
public int pop() throws Exception {
if (isEmpty()) {
throw new Exception("队列已空,不能获取元素");
}
int temp = nums[front];
front = (front + 1) % maxSize;
return temp;
}
显示队列的情况 showQueue
public void show() {
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, nums[i % maxSize]);
}
}
获取队列有效长度 size
public int size() {
return (rear + maxSize - front) % maxSize;
}
查看队列头元素 headQueue
- 此时front指向队列头节点,可以直接获取。
public int get() throws Exception {
if (isEmpty()) {
throw new Exception("队列已空,不能获取元素");
}
return nums[front];
}
应用场景
- 《java队列——queue详细分析》
- 非阻塞队列:ConcurrentLinkedQueue(无界线程安全),采用CAS机制(compareAndSwapObject原子操作)。
- 阻塞队列:ArrayBlockingQueue(有界)、LinkedBlockingQueue(无界)、DelayQueue、PriorityBlockingQueue,采用锁机制;使用 ReentrantLock 锁。
- 《LinkedList、ConcurrentLinkedQueue、LinkedBlockingQueue对比分析》