zoukankan      html  css  js  c++  java
  • 队列篇之使用数组模拟一个队列

    队列是一个有序列表, 可以使用数组实现, 也可以使用链表实现 

    队列遵守先进先出的原则

    1. 下面使用数组模拟一个队列

    public class ArrayQueueDemo {
        public static void main(String[] args) {
            ArrayQueue queue = new ArrayQueue(3);
            queue.add(1);
            queue.show();
            System.out.println("-----------------");
            queue.add(2);
            queue.show();
            System.out.println("-----------------");
            queue.add(3);
            queue.show();
            System.out.println("-----------------");
    //        System.out.println(queue.pop());
            System.out.println(queue.pop());
    ////        System.out.println(queue.pop());
    //        queue.show();
            System.out.println("-----------------");
            System.out.println(queue.peek());
            System.out.println(queue.peek());
            System.out.println(queue.peek());
            queue.show();
        }
    }
    
    /**
     * 使用数组来模拟队列
     * 该代码存在着一个明显的bug,就是数组只能使用一次, 不能复用. 原因很明显,rear指针只有++操作.
     */
    class ArrayQueue {
        /**
         * 队列最大容量
         */
        private int maxSize;
    
        /**
         * 队列头指针
         */
        private int front;
    
        /**
         * 队列尾指针
         */
        private int rear;
    
    
        /**
         * 存放数据的数组
         */
        private int[] arr;
    
    
        // 创建队列的构造器
        public ArrayQueue(int maxSize) {
            this.maxSize = maxSize;
            arr = new int[maxSize];
            front = -1;  // 指向队列头部前一个位置
            rear = -1; // 指向队列尾部指针(指队列最后一个数据)
        }
    
        /**
         * 判断队列是否已满
         *
         * @return
         */
        public boolean isFull() {
            return rear == maxSize - 1;
        }
    
        public boolean isEmpty() {
            return rear == front;
        }
    
    
        public void add(int data) {
            if (isFull()) {
                System.out.println("队列已满");
                return;
            }
    
            rear++;  // rear指针后移一位
            arr[rear] = data;
        }
    
        /**
         * 弹出队列头部的数据,
         * 其实该数据还是存在于数组中,只是front指针+1了, 不能再次获取了而已
         *
         * @return 队列头部的数据
         */
        public int pop() {
            if (isEmpty()) {
                throw new RuntimeException("队列为空, 不能取数据");
            }
            front++; // front后移一位
            return arr[front];
        }
    
        /**
         * 只是获取队列头部的数据,但并不会移除该数据(即是说front指针不会发生变化)
         * @return 队列头部的数据
         */
        public int peek() {
            if (isEmpty()) {
                throw new RuntimeException("队列为空, 不能peek数据");
            }
            return arr[front + 1]; // 为什么加1? 是因为我们默认front指向数组数据的前一位
        }
    
    
        public void show() {
            if (isEmpty()) {
                System.out.println("队列空,无数据");
                return;
            }
            for (int i = 0; i < arr.length; i++) {
                System.out.printf("arr[%d] = %s
    ", i, arr[i]);
            }
        }
    }

    2. jdk中ArrayBlockingQueue实现方式

    2.1 构造器

      public ArrayBlockingQueue(int capacity) {
            this(capacity, false);
        }
        public ArrayBlockingQueue(int capacity, boolean fair) {
            if (capacity <= 0)
                throw new IllegalArgumentException();
            this.items = new Object[capacity];
            lock = new ReentrantLock(fair);
            notEmpty = lock.newCondition();
            notFull =  lock.newCondition();
        }

    很明显, 通过构造器我们可以知道 , ArrayBlockingQueue是一个有界队列 ,其底层使用了一个Object类型的数组items来存储数据.

    2.2 添加元素

    因为添加元素到队列的底层方法都是使用offer方法,所以我们直接看offer方法源码即可

    public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 如果队列中的元素个数count等于数据组items的长度,说明队列已满,直接返回false,添加元素失败
            if (count == items.length)
                return false;
            else {
                enqueue(e); // 添加元素
                return true;
            }
        } finally {
            lock.unlock();
        }
    }
    private void enqueue(E x) {
        final Object[] items = this.items;
        items[putIndex] = x;  // 将元素x放到数组items的putIndex位置
        if (++putIndex == items.length) // 表示items已经满了
            putIndex = 0;  // putIndex重新置为0
        count++; // 队列元素加1 
        notEmpty.signal();
    }

    源码很简单,锁不是本篇的关注点. 值得一提的是 if (++putIndex == items.length) { putIndex = 0; }这行代码挺有意思的, 它解决了数组重复使用问题.

    2.3 获取元素

        private E dequeue() {
            final Object[] items = this.items;
            E x = (E) items[takeIndex];// 获取takeIndex位置的元素,从0开始,就是队列的头部
            items[takeIndex] = null;  // 将数组中takeIndex置空
            if (++takeIndex == items.length)  // 如果队列中的元素取完了,takeIndex重置为0,下次继续从头部开始获取元素
                takeIndex = 0;
            count--;  // 队列中的总元素个数减1
            if (itrs != null)   // 没用到, 没细看
                itrs.elementDequeued();
            notFull.signal();  
            return x; // 返回取出的元素
        }

    2.4 总结

    ArrayBlockingQueue底层使用了一个Object类型的数组存储数据, 同时它维护了两个指针putIndex和takeIndex,它们的默认值都是0, 其中putIndex与offer操作相关,即是每添加一个元素,putIndex的值加1, 如果++putIndex等于数组的length,说明队列已满(其实是数组满了), 这时将putIndex重置为0. takeIndex与putIndex类似,不再赘述.

  • 相关阅读:
    Value '0000-00-00' can not be represented as java.sql.Date
    mysql建表设置两个默认CURRENT_TIMESTAMP的技巧
    PowerDesigner 的mysql PDM 的COMMENT注释
    tomcat配置及优化
    Tomcat7调优及JVM性能优化for Linux环境
    maven混淆Java代码
    通过Maven配置测试环境和开发环境连接不同的数据库
    删除电脑中用强制删除不能删除的bat命令脚本
    在Linux中设置共享目录
    ftp以及smb的配置
  • 原文地址:https://www.cnblogs.com/z-qinfeng/p/12128026.html
Copyright © 2011-2022 走看看