線性表結構:隊列

什麼是隊列

隊列是一種先進入的元素先出的線性表結構。這個特性正好和棧的特性相反。

我們知道,棧只支持兩個基本操作:入棧 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併發包下面的隊列實現。

隊列的應用場景

隊列可以應用在任何有限資源池中,用於排隊請求,比如數據庫連接池等。實際上,對於大部分資源有限的場景,當沒有空閒資源時,基本上都可以通過“隊列”這種數據結構來實現請求排隊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章