Java數據結構之---Queue隊列
隊列(簡稱作隊,Queue)也是一種特殊的線性表,隊列的數據元素以及數據元素間的邏輯關系和線性表完全相同,其差別是線性表允許在任意位置插入和刪除,而隊列只允許在其一端進行插入操作在其另一端進行刪除操作。
隊列中允許進行插入操作的一端稱為隊尾,允許進行刪除操作的一端稱為隊頭。隊列的插入操作通常稱作入隊列,隊列的刪除操作通常稱作出隊列。最簡單的例子就是我們平時的排隊,先進先出。
順序隊列的存儲結構
下圖是一個有6個存儲空間的順序隊列的動態示意圖,圖中front指示隊頭,rear指示隊尾。
~順序隊列的假溢出現象
假溢出是由於隊尾rear的值和隊頭front的值不能由所定義數組下界值自動轉為數組上界值而產生的。因此,解決的方法是把順序隊列所使用的存儲空間構造成一個邏輯上首尾相連的循環隊列( Circular Queue)。
~解決方法
當rear和front達到maxSize-1后,再加1就自動到0。這樣,就不會出現順序隊列數組的頭部已空出許多存儲空間,但隊尾卻因數組下標越界而引起溢出的假溢出問題。這里在代碼里面會詳細解釋~
在操作完成后,該隊列中會有兩個空格沒有數據元素保存,造成資源浪費,這就是假溢出現象。
//==========================
//使用自定義的queue接口

1 //隊列接口 2 public interface Queue { 3 4 // 入隊 5 public void append(Object obj) throws Exception; 6 7 // 出隊 8 public Object delete() throws Exception; 9 10 // 獲得對頭元素 11 public Object getFront() throws Exception; 12 13 // 判斷是否為空 14 public boolean isEmpty(); 15 16 }
//循環鏈表的具體實現
1 /* 2 * 循環順序隊列 3 */ 4 public class CircleSequenceQueue implements Queue { 5 6 static final int defaultsize = 10;// 默認隊列的長度 7 int front; // 對頭 8 int rear; // 隊尾 9 int count;// 統計元素個數的計數器 10 int maxSize; // 隊的最大長度 11 Object[] queue; // 隊列,使用數組實現 12 13 // 默認構造 14 public CircleSequenceQueue() { 15 init(defaultsize); 16 } 17 18 public CircleSequenceQueue(int size) { 19 // 通過給定長度進行構造 20 init(size); 21 } 22 23 public void init(int size) { 24 maxSize = size; 25 front = rear = 0; 26 count = 0; 27 queue = new Object[size]; 28 } 29 30 @Override 31 public void append(Object obj) throws Exception { 32 // TODO Auto-generated method stub 33 if (count > 0 && front == rear) { 34 throw new Exception("隊列已滿"); 35 } 36 // 隊尾插入數據 37 queue[rear] = obj; 38 // 通過這種方法讓對標索引值不停的重復!!! 39 rear = (rear + 1) % maxSize; 40 count++; 41 } 42 43 @Override 44 public Object delete() throws Exception { 45 // TODO Auto-generated method stub 46 if (isEmpty()) { 47 throw new Exception("隊列為空"); 48 } 49 // 去除對頭的元素,同時修改對頭的索引值 50 Object obj = queue[front]; 51 // 對頭索引值,一樣通過+1驅魔運算來實現循環索引效果 52 front = (front + 1) % maxSize; 53 count--; 54 return obj; 55 } 56 57 @Override 58 public Object getFront() throws Exception { 59 // TODO Auto-generated method stub 60 if (!isEmpty()) { 61 return queue[front]; 62 } else { 63 // 對為空返回null 64 return null; 65 } 66 } 67 68 @Override 69 public boolean isEmpty() { 70 // TODO Auto-generated method stub 71 return count == 0; 72 } 73 74 }
//得到循環鏈表后,對其進行應用,之類主要是模擬賣票窗口
· 實例:使用順序循環隊列和多線程實現一個排隊買票的例子。
· 生產者(等候買票)
· 消費者 (買票離開)
//代碼的分割線,使用生產者消費者模式進行設計,主要是使用同步機制
1 //賣票窗口 2 public class WindowQueue { 3 4 // 賣票的隊列默認長度10 5 int maxSize = 10; 6 CircleSequenceQueue queue = new CircleSequenceQueue(maxSize); 7 // 用來統計賣票的數量,一天最多賣100張票? 8 int num = 0; 9 boolean isAlive = true; // 判斷是否繼續賣票 10 11 // 排隊買票,使用同步機制 12 public synchronized void producer() throws Exception { 13 // count隊列中的元素個數,如果該值小於maxSize則可以買票 14 if (queue.count < maxSize) { 15 queue.append(num++); // 等待買票的數量+1 16 System.out.println("第" + num + "個客戶排隊等待買票"); 17 this.notifyAll(); // 通知賣票線程可以賣票了 18 } 19 // 如果滿了 20 else { 21 try { 22 23 System.out.println("隊列已滿...請等待"); 24 this.wait(); // 隊列滿時,排隊買票線程等待,其實等待賣票隊伍里面離開一個人后來喚醒自己 25 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 } 31 32 // 排隊賣票,使用同步機制 33 public synchronized void consumer() throws Exception { 34 // count隊列中的元素個數,如果該值大於0,則說明有票可以繼續賣票 35 if (queue.count > 0) { 36 37 Object obj = queue.delete(); 38 // 第幾個人買到票了 39 int temp = Integer.parseInt(obj.toString()); 40 System.out.println("第" + (temp + 1) + "個客戶排隊買到票離開隊列"); 41 // 如果當前隊列為空,並且賣出票的數量的大於等於100說明賣票要結束 42 if (queue.isEmpty() && this.num >= 100) { 43 this.isAlive = false; 44 } 45 // 排隊隊伍離開一個人,可以進來一個人進行買票了。 46 this.notifyAll(); // 通知買票線程可以買了,喚醒買票線程 47 } 48 // 如果滿了 49 else { 50 try { 51 52 System.out.println("隊列已空...請進入隊伍准備買票"); 53 this.wait();// 隊列空時,排隊賣票線程等待,其實等待買票隊伍里面進來一個人后買票來喚醒自己 54 55 } catch (Exception e) { 56 e.printStackTrace(); 57 } 58 } 59 } 60 }
//下面的兩個類是生產者與消費者的具體實現,實現runnable接口
1 //買票者 2 3 public class Producer implements Runnable { 4 // 買票窗口 5 WindowQueue queue; 6 7 // 保證和消費者使用同一個對象 8 public Producer(WindowQueue queue) { 9 this.queue = queue; 10 } 11 12 @Override 13 public void run() { 14 // TODO Auto-generated method stub 15 // 16 while (queue.num < 100) { 17 try { 18 //執行買票,消費者 19 queue.producer(); 20 } catch (Exception e) { 21 e.printStackTrace(); 22 } 23 } 24 } 25 }
1 //賣票者 2 public class Consumer implements Runnable { 3 WindowQueue queue; 4 5 // 保證賣票與買票同步 6 public Consumer(WindowQueue queue) { 7 this.queue = queue; 8 } 9 10 @Override 11 public void run() { 12 // 判斷是否可以繼續賣票 13 while (queue.isAlive) { 14 try { 15 // 賣票 16 queue.consumer(); 17 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } 21 } 22 } 23 }
//測試。。。。。

1 public class Test { 2 public static void main(String[] args) throws Exception { 3 /* 4 * CircleSequenceQueue queue = new CircleSequenceQueue(); 5 * queue.append("a"); queue.append("b"); queue.append("c"); 6 * queue.append("d"); queue.append("e"); 7 * 8 * while (!queue.isEmpty()) { System.out.print(queue.delete() + " "); } 9 */ 10 11 // 賣票與買票模擬,使用同一個窗口對象 12 WindowQueue queue = new WindowQueue(); 13 // 生產者 14 Producer P = new Producer(queue); 15 // 消費者 16 Consumer c = new Consumer(queue); 17 18 // 排隊買票線程 19 Thread pThread = new Thread(P); 20 // 買票線程 21 Thread cThread = new Thread(c); 22 23 pThread.start(); // 開始排隊買票 24 cThread.start(); // 賣票 25 } 26 }