[資料結構與演算法]佇列Queue 的多種實現

weixin_34126215發表於2015-02-13
 1 package queue;
 2 
 3 /**
 4  * 佇列介面
 5  * @author jzj
 6  *
 7  * @param <E>
 8  */
 9 public interface Queue<E> {
10     //入隊
11     public void enqueue(E e);
12 
13     //出隊
14     public E dequeue();
15 
16     //取佇列第一個
17     public E front();
18 
19     //佇列是否為空
20     public boolean isEmpty();
21 
22     //佇列大小
23     public int size();
24 }
  1 package queue;
  2 
  3 /**
  4  * 迴圈佇列,使用陣列實現
  5  * @author jzj
  6  */
  7 public class ArrayQueue<E> implements Queue<E> {
  8 
  9     private Object lock = new Object();
 10 
 11     // 佇列大小
 12     private int size = 1000;
 13 
 14     // 佇列
 15     private E[] arrStr = (E[]) new Object[size];
 16 
 17     // 佇列指標
 18     private int head = 0;
 19 
 20     // 佇列尾指標
 21     private int tail = 0;
 22 
 23     /**
 24      * 入隊
 25      * @param o
 26      */
 27     public void enqueue(E o) {
 28         synchronized (lock) {
 29 
 30             // 如果佇列已滿
 31             while ((tail + 1) % size == head) {
 32                 try {
 33                     System.out.println("佇列已滿," + Thread.currentThread().getName()
 34                             + " 執行緒阻塞...");
 35                     // 佇列滿時執行緒阻塞
 36                     lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立
 37                 } catch (InterruptedException e) {
 38                     e.printStackTrace();
 39                 }
 40             }
 41             // 如果佇列未滿
 42             arrStr[tail] = o;
 43             // 指標下移
 44             tail = (tail + 1) % size;
 45             // 入隊後通知消費執行緒
 46             lock.notifyAll();
 47         }
 48     }
 49 
 50     /**
 51      * 出隊
 52      * @return
 53      */
 54     public E dequeue() {
 55         synchronized (lock) {
 56             // 如果佇列為空
 57             while (head == tail) {
 58                 try {
 59                     System.out.println("佇列為空," + Thread.currentThread().getName()
 60                             + " 執行緒阻塞...");
 61                     // 佇列空時執行緒阻塞
 62                     lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立
 63                 } catch (InterruptedException e) {
 64                     e.printStackTrace();
 65                 }
 66             }
 67             // 佇列非空
 68             E tempStr = arrStr[head];
 69 
 70             arrStr[head] = null;//注,這裡出隊後釋放物件,加快回收,不然大的物件可能造記憶體洩露
 71             head = (head + 1) % size;
 72 
 73             // 出隊後通知生產者
 74             lock.notifyAll();
 75             return tempStr;
 76 
 77         }
 78     }
 79 
 80     //取佇列第一個
 81     public E front() {
 82         synchronized (lock) {
 83             // 如果佇列為空
 84             while (head == tail) {
 85                 try {
 86                     System.out.println("佇列為空," + Thread.currentThread().getName()
 87                             + " 執行緒阻塞...");
 88                     // 佇列空時執行緒阻塞
 89                     lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立
 90                 } catch (InterruptedException e) {
 91                     e.printStackTrace();
 92                 }
 93             }
 94             // 佇列非空
 95             return arrStr[head];
 96         }
 97     }
 98 
 99     //佇列是否為空
100     public boolean isEmpty() {
101         return head == tail;
102     }
103 
104     //佇列大小
105     public int size() {
106 
107         return Math.abs(tail - head);
108     }
109 }

Jdk1.5已有java.util.Queue介面

使用1.5中併發新特性實現迴圈佇列:來源於 API java.util.concurrent.locks.Condition

 1 //迴圈緩衝佇列,從隊首放,從隊尾出。犧牲陣列的最後一個元素,用來判斷佇列是否滿
 2 class BoundedBuffer {
 3     final Lock lock = new ReentrantLock();//可重入鎖
 4     final Condition notFull = lock.newCondition();//緩衝空條件物件
 5     final Condition notEmpty = lock.newCondition();//緩衝非空條件物件
 6 
 7     final Object[] items = new Object[100];//緩衝區
 8     int putptr/*隊首*/, takeptr/*隊尾*/, count/*可用個數*/;
 9 
10     //將元素放入緩衝,供生產執行緒呼叫
11     public void put(Object x) throws InterruptedException {
12         lock.lock();//獲取鎖
13         try {
14             //注,要將await置於迴圈中,這也wait道理一樣
15             while (count == items.length)
16                 notFull.await();//緩衝滿時等待,且釋放鎖
17             items[putptr] = x;
18             //如果隊首指標指向陣列最後索引時,重新指向陣列第一個元素位置
19             if (++putptr == items.length)
20                 putptr = 0;
21             ++count;//迴圈佇列中存入的元素個數
22             notEmpty.signal();//放入元素後通知其它消費執行緒可以消費了
23         } finally {
24             lock.unlock();//釋放鎖
25         }
26     }
27 
28     //從緩衝取出元素,供消費執行緒呼叫
29     public Object take() throws InterruptedException {
30         lock.lock();
31         try {
32             while (count == 0)
33                 notEmpty.await();//如果緩衝空則等待
34             Object x = items[takeptr];
35             //如果隊尾指標指向陣列最後索引時,重新指向陣列第一個元素位置
36             if (++takeptr == items.length)
37                 takeptr = 0;
38             --count;
39             notFull.signal();//取出後會有空間,通知生產執行緒
40             return x;
41         } finally {
42             lock.unlock();
43         }
44     }
45 }
 1 package queue;
 2 
 3 import java.util.LinkedList;
 4 
 5 /**
 6  * 使用 LinkedList 實現佇列
 7  * @author jzj
 8  *
 9  * @param <E>
10  */
11 public class LinkedListQueue<E> implements Queue<E> {
12     private LinkedList<E> linkedList = new LinkedList<E>();
13 
14     //入隊
15     public void enqueue(E e) {
16         linkedList.addLast(e);
17     }
18 
19     //出隊
20     public E dequeue() {
21         return linkedList.removeFirst();
22     }
23 
24     //取佇列第一個
25     public E front() {
26         return linkedList.getFirst();
27     }
28 
29     //佇列是否為空
30     public boolean isEmpty() {
31         return linkedList.isEmpty();
32     }
33 
34     //佇列大小
35     public int size() {
36         return linkedList.size();
37     }
38 }
 1 package queue;
 2 
 3 import java.util.NoSuchElementException;
 4 
 5 /**
 6  * 使用 迴圈雙向鏈 實現佇列
 7  * 
 8  * @author jzj
 9  *
10  * @param <E>
11  */
12 public class LinkedQueue<E> implements Queue<E> {
13     private class Entry {
14         E element;//資料域
15         Entry next;//後驅節點
16         Entry previous;//前驅節點
17 
18         /**
19          * @param element 資料域
20          * @param next 後驅節點
21          * @param previous 前驅節點
22          */
23         Entry(E element, Entry next, Entry previous) {
24             this.element = element;
25             this.next = next;
26             this.previous = previous;
27         }
28     }
29 
30     /*
31      * 頭節點,永遠代表頭。鏈中無結節時節點中的前後驅指標域指向自己,當有元素時
32      * 頭節點的前驅指標域previous指向鏈中最後一個節點,而next指標域指向鏈中的
33      * 第一個節點
34      */
35     private Entry header = new Entry(null, null, null);
36 
37     private int size = 0;//連結串列中節點個數,但不包括頭節點header
38 
39     public LinkedQueue() {
40         //初始化時頭的前後驅指標都指向自己
41         header.next = header.previous = header;
42     }
43 
44     //入隊,在連結串列中的最後位置加入元素,相當於 LinkedList 的add、addBefore、addLast方法實現
45     public void enqueue(E e) {
46         //在雙向鏈後面加入元素
47         Entry newEntry = new Entry(e, header, header.previous);
48         //讓新元素的前驅節點(新增前鏈的最後點)的前驅指向新加元素
49         newEntry.previous.next = newEntry;
50         //讓新元素的後驅節點(header頭節點)的前驅指向新加元素
51         newEntry.next.previous = newEntry;
52         size++;
53     }
54 
55     //出隊,從連結串列中的第一個元素開始出隊,相當於 LinkedList 的removeFirst方法實現
56     public E dequeue() {
57         //要出隊(刪除)的節點的資料域
58         E first = header.next.element;
59         Entry e = header.next;
60         //要刪除的節點為頭節點時不能刪除
61         if (e == header) {
62             throw new NoSuchElementException();
63         }
64 
65         //將刪除節點的前驅節點的next域指標指向刪除節點的後驅節點
66         e.previous.next = e.next;
67         //將刪除節點的後驅節點的previous域指標指向刪除節點的前驅節點
68         e.next.previous = e.previous;
69         size--;
70         return first;
71     }
72 
73     //取佇列的第一個元素,相當於 LinkedList 的getFirst方法實現
74     public E front() {
75         if (size == 0)
76             throw new NoSuchElementException();
77 
78         return header.next.element;
79     }
80 
81     //佇列是否為空
82     public boolean isEmpty() {
83         return size == 0;
84     }
85 
86     //佇列大小
87     public int size() {
88         return size;
89     }
90 }

相關文章