目錄
一、what is 隊列?
1.1 現實中的隊列
隊列隊列就是要排隊啊,排隊就是要等待啊,現實中有哪些需要等待的?
你們有沒有這樣的經歷,在用電腦的時候,鼠標點什麼似乎都沒用,雙擊任何快捷方式都不動彈,處於疑似死機的狀態。但等一會後,電腦又似乎醒過來了,又把你剛纔點擊的所有操作全部按順序執行一遍。這其實是因爲操作系統中的多個程序因需要通過一個通道輸出,而按先後次序排隊等待造成的。
還有你在超市的某個收銀通道排隊付款時,也是一樣按照排隊順序來進行。
還有你給某個客服打電話時,經常遇到“人工坐席忙,請稍後再撥”的提示,那也是因爲客戶太多而客服少,只有等上一個完事才能處理你的業務
.....
還有好多隊列的例子,那我們可以概括一下這些例子的一些特點:
- 你在排隊的時候,永遠是在隊尾加入(插隊估計要被打)
- 隊首的人付完錢會離開
- 排在你前面的人先離開
1.2 代碼中的隊列
代碼中的隊列和現實中的隊列思想上實際是一致的:
- 隊列是一種先進先出(First in First Out)的線性表,簡稱FIFO
- 允許插入的一端稱爲隊尾,允許刪除的一端稱爲隊頭
- 順序插入,不允許插隊
1.3 隊列分類
按照存儲方式(既然是線性表,按照存儲方式那就有兩種存儲方式):
- 基於數組的順序存儲方式
- 基於鏈表的鏈式存儲方式
按照實現方式也分爲:
- 單向隊列(Queue):只能在一端插入數據,另一端刪除數據
- 雙向隊列(Deque):每一端都可以進行插入數據和刪除數據操作
1.4 隊列適用範圍
如果是一些及時消息的處理,並且處理時間很短的情況下是不需要使用隊列的。
如果在消息處理的時候特別費時間,這個時候如果有新的消息來了,就只能處於阻塞狀態,造成用戶等待。這個時候在項目中引入隊列是十分有必要的。當我們接受到消息後,先把消息放到隊列中,然後再用新的線程進行處理,這個時候就不會有消息的阻塞了。
二、隊列使用
Queue接口與List、Set同一級別,都是繼承了Collection接口。
Queue的實現有PriorityQueue、ArrayDeque、LinkedList。其中ArrayDeque、LinkedList是實現的其子接口Deque。
Queue中包含的方法如下:
- add(E e)將指定元素加入到隊列尾部,會拋出IllegalStateException異常
- offer(E e) 將指定元素加入到隊列尾部,會返回 false
- remove() 返回隊列頭部元素並刪除,如果隊列爲空,會拋NoSuchElementException
- poll() 返回隊列頭部元素並刪除,如果隊列爲空,返回null
- element() 返回隊列頭部元素不刪除,如果隊列爲空,會拋NoSuchElementException
- peek() 返回隊列頭部元素不刪除,如果隊列爲空,返回null
public class Demo {
public static void main(String[] args) {
Queue queue = new ArrayDeque();
queue.offer("1");
queue.offer("2");
queue.offer("3");
queue.add("4");
queue.add("5");
queue.add("6");
queue.stream().forEach(item ->{
System.out.println(item);
});
// 返回隊首的元素
Object firstElement = queue.element();
System.out.println("隊首的元素:" + firstElement);
// 移除並返回隊列頭部的元素
System.out.println("移除並返回隊列頭部的元素:" + queue.remove());
System.out.println("移除後:");
queue.stream().forEach(item ->{
System.out.println(item);
});
}
}
基於LinkedList:
public class Demo {
public static void main(String[] args) {
Queue queue = new LinkedList();
queue.offer("1");
queue.offer("2");
queue.offer("3");
queue.add("4");
queue.add("5");
queue.add("6");
queue.stream().forEach(item ->{
System.out.println(item);
});
// 返回隊首的元素
Object firstElement = queue.element();
System.out.println("隊首的元素:" + firstElement);
// 移除並返回隊列頭部的元素
System.out.println("移除並返回隊列頭部的元素:" + queue.remove());
System.out.println("移除後:");
queue.stream().forEach(item ->{
System.out.println(item);
});
}
}
兩種結果一樣:
1
2
3
4
5
6
隊首的元素:1
移除並返回隊列頭部的元素:1
移除後:
2
3
4
5
6
LinkedList類實現了Queue接口,因此我們可以把LinkedList當成Queue來用。
二、隊列的實現
前面說過隊列按照存儲結構可以分爲順序隊列和鏈式隊列。順序隊列採用數組實現,鏈式隊列採用鏈表的方式實現。
順序隊列實現:
/**
* Feng, Ge 2020/3/13 0013 16:18
*/
public class MyArrayQueue {
private Object[] data;
// 最大容量
private int maxSize = 0;
// 隊首位置
private int head = 0;
// 隊尾位置
private int tail = 0;
/**
* 構造方法,初始化隊列大小
*/
public MyArrayQueue(int size) {
data = new Object[size];
maxSize = size;
}
public MyArrayQueue() {
this(6);
}
/**
* 判斷隊列是否爲空
*/
public boolean isEmpty() {
return tail == head ? true : false;
}
/**
* 判斷隊列是否已滿
*/
public boolean isMax() {
return tail >= maxSize ? true : false;
}
/**
* 加入隊列
*/
public boolean offer(Object obj) {
if (isMax()) {
System.out.println("隊列已存滿");
return false;
}
// 隊尾加入元素
data[tail] = obj;
tail++;
return true;
}
/**
* 刪除隊首元素,並返回隊首元素
*/
public Object poll() {
if (isEmpty()) {
System.out.println("隊列爲空");
}
Object fisrtElement = data[head];
// 從 head+1 位置開始到tail位置的數據向前移動一位
System.arraycopy(data, head+1, data, head, tail-1);
tail--;
return fisrtElement;
}
public static void main(String[] args) {
MyArrayQueue myArrayQueue = new MyArrayQueue();
myArrayQueue.offer(0);
myArrayQueue.offer(1);
myArrayQueue.offer(2);
myArrayQueue.offer(3);
myArrayQueue.offer(4);
myArrayQueue.offer(5);
System.out.println("添加順序:");
for (int i = 0; i < myArrayQueue.data.length; i++) {
System.out.println(myArrayQueue.data[i]);
}
System.out.println("隊首元素:" + myArrayQueue.poll());
myArrayQueue.offer(6);
System.out.println("刪除後再添加的順序:");
for (int i = 0; i < myArrayQueue.data.length; i++) {
System.out.println(myArrayQueue.data[i]);
}
}
}
添加順序:
0
1
2
3
4
5
隊首元素:0
刪除後再添加的順序:
1
2
3
4
5
6
鏈式隊列實現:
這個實現和之前寫過的一篇 《JAVA集合類(詳解)》裏的LinkedList幾乎一樣,可以跳過去看看,地址:https://blog.csdn.net/weixin_41231928/article/details/103413167