在上一篇随笔中使用数据模拟了一个队列,但是有缺陷, 数组只能使用一次,原因是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

posted on 2020-01-01 23:50  显示账号  阅读(222)  评论(0编辑  收藏  举报