在上一篇随笔中使用数据模拟了一个队列,但是有缺陷, 数组只能使用一次,原因是index指针一直往上++, 不能回到数组之前的位置.
ArrayBlockingQueue源码给出了一种实现方案, 它是在判断队列(数组)满了之后,直接将putIndex重置为0, 这样offer元素到队列时,又可以利用数组之前的空间了.
下面使用环型数组的方式来实现数组的重复利用.
代码如下
public class CircleArrayQueueDemo { public static void main(String[] args) { // 入参为4,但是队列最多只能存放3个元素,因为在判断队列是否满的时 (rear+1) 了 CircleArrayQueue circleArrayQueue = new CircleArrayQueue(4); circleArrayQueue.add(1); circleArrayQueue.show(); System.out.println("-----------------------------"); circleArrayQueue.add(2); circleArrayQueue.show(); System.out.println("-----------------------------"); circleArrayQueue.add(3); circleArrayQueue.show(); System.out.println("-----------------------------"); circleArrayQueue.pop(); // 将队列首位的元素取走 circleArrayQueue.add(4); circleArrayQueue.show(); System.out.println("-----------------------------"); circleArrayQueue.pop(); circleArrayQueue.add(5); circleArrayQueue.show(); } } class CircleArrayQueue { /** * 队列最大容量 */ private int maxSize; /** * 队列头指针,指向队列的第一个元素, 初始值0 */ private int front; /** * 队列尾指针, 初始始0 */ private int rear; /** * 存放数据的数组 */ private int[] arr; // 创建队列的构造器 public CircleArrayQueue(int maxSize) { this.maxSize = maxSize; arr = new int[maxSize]; } /** * 判断队列是否已满 * * @return */ public boolean isFull() { return (rear + 1) % maxSize == front; // 因为这儿加了1,所以队列中能存放元素的个数应该等于 (maxSize -1) } public boolean isEmpty() { return rear == front; } public void add(int data) { if (isFull()) { System.out.println("队列已满"); return; } // 因为rear直接指向的是队列最后一个元素的后一个位置,所以添加元素时,直接放到rear指针处即可 arr[rear] = data; // 添加元素之后,重新计算rear的位置 rear = (rear + 1) % maxSize; } /** * @return 队列头部的数据 */ public int pop() { if (isEmpty()) { throw new RuntimeException("队列为空, 不能取数据"); } // 因为front指向了队列的第一个元素, int value = arr[front]; front = (front + 1) % maxSize; // 将front 后移一位, 取模是为了重回首位 return value; } /** * 显示队列的头元素 * * @return 队列头部的数据 */ public int peek() { if (isEmpty()) { throw new RuntimeException("队列为空, 不能peek数据"); } return arr[front]; } public void show() { if (isEmpty()) { System.out.println("队列空,无数据"); return; } // 从front开始遍历 for (int i = front; i < front + count(); i++) { System.out.printf("arr[%d] = %s\n", i % maxSize, arr[i % maxSize]); } } /** * 当前队列元素的个数 * * @return */ public int count() { return (rear + maxSize - front) % maxSize; // 当前队列的有效个数 } }
总的来说,该方案使用取模的方式来实现数组index的移动,这很容易理解.
但是isFull方法却有点意思 .
假设队列maxSize = 3
添加第1个元素1:
add(1) 之后 rear = 1, front = 0
添加第2个元素2:
add(2) 之后 rear = 2, front = 0
此时,再添加第3个元素3:
add(3) 时,先执行isFull方法, (2+1)%3=0 , 等于front, 所以满了.
如果pop首个元素:
pop() , front = (0+1)%3 =1
再add(3): 先调用isFull方法, (2+1)%3=0 , 不等于front, 所以没满, 可以添加 arr[2] = 3
日拱一卒无有尽,功不唐捐终入海