什麼是佇列?
佇列(queue)是隻允許在一端進行插入操作,而在另一端進行刪除操作的線性表。是一種先進先出(First In First Out)的線性表,簡稱 FIFO。允許插入的一端稱為隊尾,允許刪除的一端稱為隊頭。 佇列有 2 種方式來儲存:陣列 和 連結串列。 陣列我們都知道它是預先分配好長度的,因此會出現溢位現象,而且刪除元素需要向隊頭移動一個位置,時間複雜度就變成 O(n)。因此,需要一種新的方式來解決這個問題,那就是迴圈佇列。 佇列的這種頭尾相接的順序儲存結構稱為迴圈佇列。為了避免佇列刪除元素需要移動整個佇列,使得隊頭和隊尾可以在陣列中迴圈變化。解決了移動元素的時間損耗,使得本來刪除是 O(n) 的時間複雜度變成了 O(1)。
1. 陣列式佇列
申請一片連續的儲存空間,並設定兩個指標進行管理。一個是隊頭指標front,它指向隊頭元素;另一個是隊尾指標rear,它指向下一個入隊元素的儲存位置。一般是實現迴圈佇列,隊頭和隊尾會隨著入隊和出隊的變化而變化的。例如 JDK 中 ArrayBlockingQueue 就是基於迴圈佇列來實現的。不過它會有溢位現象,一般解決方案是如果佇列滿了,設定入隊等待時間或者返回入隊不成功。一般在確定元素個數情況下使用,如果不確定元素個數,建議使用連結串列式佇列。
2. 連結串列式佇列
它就是基於連結串列儲存結構的佇列,可以動態的建立和刪除元素,不用關心佇列的長度,因此不用擔心溢位現象。新元素插入到隊尾,讀取的時候從隊頭開始,每次讀取一個元素,釋放一個元素,這就是所謂的動態建立和動態刪除。
佇列有什麼用?
1. 保證輸入順序
比如吃飯排隊,先找服務員拿個號碼,上面會寫著前面還有 n 桌,這就相當於服務員把你加入了她們店的佇列中,當有空位置時,就直接叫你入座吃飯,當沒有空位子時,要麼排隊等候,要麼換一家吃飯。這就是佇列的用處。
2. 解耦
在系統設計中,好的設計是低耦合,高內聚。意思就是一個系統只做一件事情,把一件事情做好。既方便程式碼維護,又方便擴充套件。比如又一個下單的場景,使用者下單之後需要加積分,需要給各種優惠券等等。我們就可以利用佇列來解偶,但真實使用一般是用開源的訊息佇列,如Rocket MQ,Kafka 等等。
3. 提升系統吞吐量
就拿上面訂單來說,本來以前是使用者下單,給使用者新增積分,給使用者發優惠券是一起處理成功後返回的。現在只是處理下單,然後告訴某某系統,給某某使用者加積分,給某某使用者發優惠券了,它自己並不真正做這個動作,所以會提升系統響應,當然需要保證最終一致性。一般也是用開源的訊息佇列來完成的。
佇列怎麼實現?
這裡是基於陣列的迴圈佇列實現,也就是 JDK 的 ArrayBlockingQueue。有興趣的朋友也可以看看基於連結串列的 LinkedBlockingQueue 的實現。 儲存結構
public class ArrayBlockingQueue<E> {
//用陣列來儲存元素
Object[] items;
//陣列裡出隊的下標
int takeIndex;
//入隊的下標
int putIndex;
//元素個數
int count;
}
複製程式碼
入隊
public boolean offer(E e) {
//加入陣列滿了,則返回入隊失敗
if (count == items.length) {
return false;
} else {
//獲得當前陣列
Object[] items = this.items;
//把元素 e 加入到隊尾
items[putIndex] = e;
//判斷是不是隊尾是不是倒數第二個元素
//是的話把下標為 0 置為隊尾,說明一圈了
if (++putIndex == items.length) {
putIndex = 0;
}
//總數加一
count++;
return true;
}
}
複製程式碼
出隊
public E poll() {
//空佇列則返回 null
if (count == 0) {
return null;
} else {
//獲取當前陣列
Object[] items = this.items;
//獲取隊頭元素
E item = (E) items[takeIndex];
//置空
items[takeIndex] = null;
//重置隊頭為下標 0;
if (++takeIndex == items.length) {
takeIndex = 0;
}
//總數減一
count--;
return item;
}
}
複製程式碼
總結
佇列的作用還是很大的,比如保證輸入順序,解偶,提升系統吞吐量等等,都基於佇列的原理來實現的。佇列有陣列式佇列和連結串列式佇列。陣列式佇列就是基於陣列儲存結構來實現的,陣列的優缺點它全有。連結串列式佇列就是基於連結串列儲存結構來實現的,它也包含來連結串列的優缺點。所以在使用時可以根據業務需求來選擇最優的方案。