1.特點:隊列有“先進先出”的特點,使用的場景多數用於資源池這種,例如:線程池中對等待線程的處理,數據庫連接池中對線程的管理
2. 隊列的生成有兩種方式:(1)基於數組生成,稱爲順序隊列。(2)基於鏈表生成,稱爲鏈式隊列。
3.基於數組生成隊列的程序:
(1)在隊列中進行入隊操作時,tail指針會不斷地向後移動,直到數組已經被裝滿。進行出隊操作時,head指針會不斷地從0開始向後移動,這樣會出現一個情況:(當tail指針移動到最後時,即使數組的0---head區間有位置,也不會承裝數據,造成數組資源的浪費),所以我們需要採用 “數據搬移” 的操作。
數據搬移有兩種方式:(1)每次有數據入隊,便會搬移一次,相當於每次都刪除下標爲0的數據。這樣入隊操作的時間複雜對便會從O(1)變成O(n)。(2)只有當tail指針到達最後時,並且head指針不爲0,才進行一次整體的數據搬移操作,這樣入隊操作的平均時間複雜度還是O(1)。
/*
* 基於數組實現的順序隊列*/
public class ArrayQueue {
/*定義隊列中承裝數據的數組*/
private Object[] elementData;
/*定義隊列的容量*/
private int capacity;
/*定義隊列的頭指針和尾指針*/
private int head;
private int tail;
public ArrayQueue(int capacity) {
this.elementData = new Object[capacity];
this.capacity = capacity;
this.head = 0;
this.tail = 0;
}
/*
* 入隊操作*/
public boolean enQueue(Object element) {
/*判斷當前隊列是否已滿*/
if(capacity == tail) return false;
elementData[tail] = element;
tail++;
return true;
}
/*
* 採用數據搬移的方法,使數組中的空閒空間,得以充分的利用*/
public boolean enQueueMove(Object element) {
/*如果尾指針tail=capacity && head=0時,表明數組已經裝滿*/
if(tail == capacity ) {
if(head == 0) return false;
/*如果尾指針tail=capacity,並且head!=0時,進行數據搬移操作*/
for(int i=head;i<tail;i++) {
elementData[i-head] = elementData[i];
}
//搬移完數據之後,重新設置head,tail的指針
tail = tail - head;
head = 0;
}
/*如果隊列後面還有剩餘的位置,那麼數據正常加入隊尾*/
elementData[tail] = element;
tail++;
return true;
}
/*
* 出隊操作*/
public Object deQueue() {
/*判斷當前隊列是否爲空*/
if(tail == head) return null;
Object element = elementData[head];
head++;
return element;
}
public static void main(String[] args) {
ArrayQueue queue = new ArrayQueue(5);
for(int i=0;i<=5;i++) {
System.out.println(queue.enQueue(i)+" , "+i);
}
for(int i=0;i<=2;i++) {
System.out.println(queue.deQueue()+" ,出隊");
}
System.out.println(queue.enQueueMove("chen")+" , chen");
System.out.println(queue.enQueueMove("chen")+" , chen");
System.out.println(queue.enQueueMove("chen")+" , chen");
System.out.println(queue.enQueueMove("chen")+" , chen");
}
}
/*基於鏈表生成的隊列*/
public class LinkedListQueue<E> {
/*定義兩個指針,head指向隊列頭部,tail指向隊列尾部*/
private Node head = null;
private Node tail = null;
/*入隊操作*/
public void push(E value) {
Node<E> node = new Node<>(value,null);
/*tail == null 表示隊列中還沒有加入元素*/
if(tail == null) {
head = node;
tail = node;
}else {
tail.setNext(node);
tail = tail.getNext();
}
}
/*出隊操作*/
public E pop() {
if(head == null) return null;
E value = (E)head.getValue();
head = head.getNext();
return value;
}
public void printAll() {
//if(head == null) return;
while (head != null) {
System.out.println(head.getValue());
head = head.getNext();
}
return;
}
private class Node<E>{
private E value;
private Node<E> next;
public Node(E value,Node<E> next) {
this.value = value;
this.next = next;
}
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
public Node<E> getNext() {
return next;
}
public void setNext(Node<E> next) {
this.next = next;
}
}
public static void main(String[] args) {
LinkedListQueue queue = new LinkedListQueue();
queue.push("1");
queue.push("2");
queue.pop();
queue.push("3");
queue.push("4");
queue.pop();
queue.push("5");
queue.pop();
queue.printAll();
}
}
基於數組生成的循環隊列,它的優點在於,在隊列中刪除數據後,空閒的空間可以得到充分的利用,並且不需要進行“搬移數據操作”
/*基於數組生成一個循環隊列*/
public class CircularQueue<E> {
/*循環隊列中的內部數組*/
private Object[] elementData;
/*循環隊列的容量*/
private int capacity;
/*循環隊列的頭指針和尾指針*/
private int head = 0;
private int tail = 0;
public CircularQueue(int capacity) {
this.elementData = new Object[capacity];
this.capacity = capacity;
}
/*入隊操作:
* 當tail指針指向數組中最後一位時,(tail+1)%capacity == head,最後一位加不上數據,
* 所以使用循環隊列會浪費一個數組空間*/
public boolean enQueue(E value) {
/*如果隊列滿了,返回false*/
if((tail+1)%capacity == head)
return false;
elementData[tail] = value;
/*因爲當前是一個環形隊列,所以判斷下一位時需要使用(tail+1) % capacity*/
tail = (tail+1) % capacity;
return true;
}
/*出隊操作*/
public E deQueue() {
if(head == tail) return null;
E value = (E)elementData[head];
head = (head + 1) % capacity;
return value;
}
public void printAll() {
while ((head+1)%capacity < tail) {
System.out.println(elementData[head]);
head = (head+1)%capacity;
}
/*因爲在while循環中,由於判斷的原因,tai指針的前一個元素遍歷不到,所以需要另外打印*/
if((head+1) % capacity == tail) {
System.out.println(elementData[head]);
}
}
public static void main(String[] args) {
/*由於在循環隊列中,最後一個位置不會承裝數據,所以如果定義數組的容量爲9,
那麼實際上只能承裝8個數據*/
CircularQueue<String> queue = new CircularQueue<>(9);
queue.enQueue("1");
queue.enQueue("2");
queue.enQueue("3");
queue.enQueue("4");
queue.deQueue();
queue.enQueue("5");
queue.enQueue("6");
queue.printAll();
}
}
阻塞隊列:
(1)定義:阻塞隊列就是在正常隊列的情況下,添加阻塞操作。當隊列爲空時,從隊列獲取數據的操作會被阻塞,直到隊列中 有數據。當隊列滿時,向隊列添加數據時會被阻塞,直到隊列中存在空閒位置。
基於阻塞隊列可以實現一個 生產者---消費者模型,並且還可以通過協調生產者和消費者的個數,來提高數據的處理效率。
線程池的底層,也是通過一個隊列實現的,當線程池中的線程全部都在工作的時候,當有新的任務請求過來時,就會將任務加入到線程池的隊列中,等待線程去執行。
實現隊列有兩種方式,數據和鏈表,基於鏈表實現的隊列,是一個沒有界限的隊列,所以當前任務等待的時間會比較長,對於時間響應敏感的系統來說,不太適合,這時可以選用基於數組實現的隊列,可以固定隊列的長度,隊列裝滿之後的任務會被拒絕掉,所以系統的響應時間會比較快。
合理的設置隊列的容量也是有講究的,容量太大,會降低系統的響應時間,容量太小,系統的資源得不到充分的利用。實際上對於大部分資源有限的場景,在沒有空閒資源時,都可以使用隊列這種數據結構來實現請求排隊。