Loading

线性表结构:队列

什么是队列

队列是一种先进入的元素先出的线性表结构。这个特性正好和栈的特性相反。

我们知道,栈只支持两个基本操作:入栈 push()和出栈 pop()。队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队 enqueue(),放一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。

img

手动实现队列

队列的实现方式也有两种,一种是通过数组实现,叫顺序队列,另外一种是通过链表实现,叫链式队列。

顺序队列

public static class MyQueue<E> {

        private Object[] array;
        private int head;
        private int tail;

        public MyQueue(int capacity) {
            this.array = new Object[capacity];
        }

        public boolean enqueue(E e) {
            array[tail] = e;
            int length = array.length;
            tail = (++tail) % length;
            if (tail == head) {
                // 扩容
                Object[] data = new Object[length * 2];
                int index = 0;
                int i = head;
                do {
                    data[index++] = array[i];
                    i = (++i) % length;
                } while (i != head);
                tail = length;
                head = 0;
                array = data;
            }
            return true;
        }

        public E dequeue() {
            if (head == tail) {
                return null;
            }
            E item = (E) array[head];
            array[head] = null;
            head = (++head) % (array.length);
            return item;
        }

    }

链式队列

 public static class MyQueue<E> {

        private Node head;
        private Node tail;

        public MyQueue() {

        }

        public MyQueue(E data) {
            head = tail = new Node(data);
        }

        public boolean enqueue(E data) {
            if (head == null) {
                this.head = new Node(data);
                this.tail = head;
            } else {
                Node node = new Node(data);
                tail.next = node;
                tail = node;
            }
            return true;
        }

        public E dequeue() {
            if (head == null) {
                return null;
            } else {
                E item = (E) head.data;
                head = head.next;
                return item;
            }
        }

    }


    public static class Node<E> {
        private E data;
        private Node next;

        public Node(E data) {
            this.data = data;
            this.next = null;
        }

        public Node(E data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

JDK 中的队列实现

如果不想自己实现队列,那么我们可以使用 JDK 提供的队列实现。

img

主要实现有:

  • ArrayDeque
  • LinkedList

下面看下使用LinkedList来实现Queue的例子:

public static class MyQueue<E> {

    private LinkedList<E> queue;

    public MyQueue() {
        this.queue = new LinkedList<E>();
    }

    public MyQueue(E e) {
        this.queue = new LinkedList<E>();
        queue.add(e);
    }

    public boolean enqueue(E e) {
        queue.offer(e);
        return true;
    }

    public E dequeue() {
        if (queue.isEmpty()) {
            return null;
        } else {
            return queue.pop();
        }
    }
}

常见的队列种类

循环队列、阻塞队列、并发队列。它们在很多偏底层系统、框架、中间件的开发中,起着关键性的作用。比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包利用 ArrayBlockingQueue 来实现公平锁等。

循环队列

在上面的顺序队列的实现过程中,我们使用了循环队列的实现方式。使用循环队列,可以避免在数组中对数据进行搬移。

阻塞队列

所谓阻塞队列是指:在队列是空的情况下从队列头部取一个元素,操作会被阻塞直到队列中被插入一个元素,在队列满的情况下,向队列中插入一个元素,操作也会被阻塞,直到其他操作从队列中取出一个元素为止。

我们发现,通过阻塞队列可以很好的实现一个生产者和消费者模式。

- ArrayBlockingQueue.java
- BlockingDeque.java
- DelayQueue.java
- LinkedBlockingDeque.java
- LinkedBlockingQueue.java
- LinkedTransferQueue.java
- PriorityBlockingQueue.java
- ScheduledThreadPoolExecutor
- SynchronousQueue.java
- TransferQueue.java

Java 中提供了以上的阻塞队列相关的实现或者接口,其中的队列实现也是线程安全的。

并发队列

线程安全的队列实现,参考Java并发包下面的队列实现。

队列的应用场景

队列可以应用在任何有限资源池中,用于排队请求,比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。

posted @ 2021-05-31 14:10  程序员自由之路  阅读(323)  评论(0编辑  收藏  举报