一、隊列(Queue)
隊列是一種特殊的線性表,它只允許在表的前段(front)進行刪除操作,只允許在表的后端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
對於一個隊列來說,每個元素總是從隊列的rear端進入隊列,然后等待該元素之前的所有元素出隊之后,當前元素才能出對,遵循先進先出(FIFO)原則。
如果隊列中不包含任何元素,該隊列就被稱為空隊列。
Java提供了一個Queue接口,並為該接口提供了眾多的實現類:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、PriorityQueue、ConcurrentLinkedQueue和SynchronousQueue。
其中常用的是:ArrayBlockingQueue、LinkedBlockingQueue和CurrentLinkedQueue,它們都是線程安全的隊列。LinkedBlockingQueue隊列的吞吐量通常比ArrayBlockingQueue隊列高,但在大多數並發應用程序中,LinkedBlockingQueue的性能要低。
除了LinkedBlockingQueue隊列之外,JDK還提供了另外一種鏈隊列ConcurrentLinkedQueue,它基於一種先進的、無等待(wait-free)隊列算法實現。
二、順序隊列存儲結構的實現
1 package com.ietree.basic.datastructure.queue; 2 3 import java.util.Arrays; 4 5 /** 6 * Created by ietree 7 * 2017/4/29 8 */ 9 public class SequenceQueue<T> { 10 11 private int DEFAULT_SIZE = 10; 12 // 保存數組的長度 13 private int capacity; 14 // 定義一個數組用於保存順序隊列的元素 15 private Object[] elementData; 16 // 保存順序隊列中元素的當前個數 17 private int front = 0; 18 private int rear = 0; 19 20 // 以默認數組長度創建空順序隊列 21 public SequenceQueue() { 22 23 capacity = DEFAULT_SIZE; 24 elementData = new Object[capacity]; 25 26 } 27 28 // 以一個初始化元素來創建順序隊列 29 public SequenceQueue(T element) { 30 31 this(); 32 elementData[0] = element; 33 rear++; 34 35 } 36 37 /** 38 * 以指定長度的數組來創建順序線性表 39 * 40 * @param element 指定順序隊列中第一個元素 41 * @param initSize 指定順序隊列底層數組的長度 42 */ 43 public SequenceQueue(T element, int initSize) { 44 45 this.capacity = initSize; 46 elementData = new Object[capacity]; 47 elementData[0] = element; 48 rear++; 49 } 50 51 /** 52 * 獲取順序隊列的大小 53 * 54 * @return 順序隊列的大小值 55 */ 56 public int length() { 57 58 return rear - front; 59 60 } 61 62 /** 63 * 插入隊列 64 * 65 * @param element 入隊列的元素 66 */ 67 public void add(T element) { 68 69 if (rear > capacity - 1) { 70 throw new IndexOutOfBoundsException("隊列已滿異常"); 71 } 72 elementData[rear++] = element; 73 74 } 75 76 /** 77 * 移除隊列 78 * 79 * @return 出隊列的元素 80 */ 81 public T remove() { 82 83 if (empty()) { 84 throw new IndexOutOfBoundsException("空隊列異常"); 85 } 86 87 // 保留隊列的rear端的元素的值 88 T oldValue = (T) elementData[front]; 89 // 釋放隊列頂元素 90 elementData[front++] = null; 91 return oldValue; 92 93 } 94 95 // 返回隊列頂元素,但不刪除隊列頂元素 96 public T element() { 97 98 if (empty()) { 99 throw new IndexOutOfBoundsException("空隊列異常"); 100 } 101 return (T) elementData[front]; 102 103 } 104 105 // 判斷順序隊列是否為空 106 public boolean empty() { 107 108 return rear == front; 109 110 } 111 112 // 清空順序隊列 113 public void clear() { 114 115 // 將底層數組所有元素賦值為null 116 Arrays.fill(elementData, null); 117 front = 0; 118 rear = 0; 119 120 } 121 122 public String toString() { 123 124 if (empty()) { 125 126 return "[]"; 127 128 } else { 129 130 StringBuilder sb = new StringBuilder("["); 131 for (int i = front; i < rear; i++) { 132 sb.append(elementData[i].toString() + ", "); 133 } 134 int len = sb.length(); 135 return sb.delete(len - 2, len).append("]").toString(); 136 } 137 138 } 139 140 }
測試類:
1 package com.ietree.basic.datastructure.queue; 2 3 /** 4 * Created by ietree 5 * 2017/4/30 6 */ 7 public class SequenceQueueTest { 8 9 public static void main(String[] args) { 10 11 SequenceQueue<String> queue = new SequenceQueue<String>(); 12 // 依次將4個元素加入到隊列中 13 queue.add("aaaa"); 14 queue.add("bbbb"); 15 queue.add("cccc"); 16 queue.add("dddd"); 17 System.out.println(queue); 18 19 System.out.println("訪問隊列的front端元素:" + queue.element()); 20 21 System.out.println("第一次彈出隊列的front端元素:" + queue.remove()); 22 23 System.out.println("第二次彈出隊列的front端元素:" + queue.remove()); 24 25 System.out.println("兩次remove之后的隊列:" + queue); 26 } 27 28 }
程序輸出:
[dddd, cccc, bbbb, aaaa]
訪問棧頂元素:dddd
第一次彈出棧頂元素:dddd
第二次彈出棧頂元素:cccc
兩次pop之后的棧:[bbbb, aaaa]
三、隊列的鏈式存儲結構實現
1 package com.ietree.basic.datastructure.queue; 2 3 /** 4 * Created by ietree 5 * 2017/4/30 6 */ 7 public class LinkQueue<T> { 8 9 // 定義一個內部類Node,Node實例代表鏈隊列的節點 10 private class Node { 11 12 // 保存節點的數據 13 private T data; 14 // 指向下個節點的引用 15 private Node next; 16 17 // 無參構造器 18 public Node() { 19 } 20 21 // 初始化全部屬性的構造器 22 public Node(T data, Node next) { 23 24 this.data = data; 25 this.next = next; 26 27 } 28 29 } 30 31 // 保存該鏈隊列的頭節點 32 private Node front; 33 // 保存該鏈隊列的尾節點 34 private Node rear; 35 // 保存該鏈隊列中已包含的節點數 36 private int size; 37 38 // 創建空鏈隊列 39 public LinkQueue() { 40 // 空鏈隊列,front和rear的值都為null 41 front = null; 42 rear = null; 43 } 44 45 // 以指定數據元素來創建鏈隊列,該鏈隊列只有一個元素 46 public LinkQueue(T element) { 47 48 front = new Node(element, null); 49 // 只有一個節點,front、rear都是指向該節點 50 rear = front; 51 size++; 52 53 } 54 55 // 返回鏈隊列的長度 56 public int length() { 57 58 return size; 59 60 } 61 62 // 將新元素加入隊列 63 public void add(T element) { 64 // 如果該鏈隊列還是空鏈隊列 65 if (front == null) { 66 front = new Node(element, null); 67 // 只有一個節點,front、rear都是指向該節點 68 rear = front; 69 } else { 70 // 創建新節點 71 Node newNode = new Node(element, null); 72 // 讓尾節點的next指向新增的節點 73 rear.next = newNode; 74 rear = newNode; 75 } 76 size++; 77 } 78 79 // 刪除隊列front端的元素 80 public T remove() { 81 82 Node oldfront = front; 83 // 讓front引用指向原隊列頂元素的下一個元素 84 front = front.next; 85 // 釋放原隊列頂元素的next引用 86 oldfront.next = null; 87 size--; 88 return oldfront.data; 89 90 } 91 92 // 訪問隊列頂元素,但不刪除隊列頂元素 93 public T element() { 94 95 return rear.data; 96 97 } 98 99 // 判斷鏈隊列是否為空隊列 100 public boolean empty() { 101 102 return size == 0; 103 104 } 105 106 // 請空鏈隊列 107 public void clear() { 108 // 將front、rear兩個節點賦為null 109 front = null; 110 rear = null; 111 size = 0; 112 } 113 114 public String toString() { 115 116 // 鏈隊列為空隊列時 117 if (empty()) { 118 return "[]"; 119 } else { 120 StringBuilder sb = new StringBuilder("["); 121 for (Node current = front; current != null; current = current.next) { 122 sb.append(current.data.toString() + ", "); 123 } 124 int len = sb.length(); 125 return sb.delete(len - 2, len).append("]").toString(); 126 } 127 128 } 129 130 }
測試類:
1 package com.ietree.basic.datastructure.queue; 2 3 /** 4 * Created by ietree 5 * 2017/4/30 6 */ 7 public class LinkQueueTest { 8 9 public static void main(String[] args) { 10 11 LinkQueue<String> queue = new LinkQueue<String>("aaaa"); 12 // 依次將4個元素加入到隊列中 13 queue.add("bbbb"); 14 queue.add("cccc"); 15 queue.add("dddd"); 16 System.out.println(queue); 17 18 // 刪除一個元素后 19 queue.remove(); 20 System.out.println("刪除一個元素后的隊列:" + queue); 21 22 // 再添加一個元素 23 queue.add("eeee"); 24 System.out.println("再次添加元素后的隊列:" + queue); 25 26 } 27 28 }
程序輸出:
[aaaa, bbbb, cccc, dddd]
刪除一個元素后的隊列:[bbbb, cccc, dddd]
再次添加元素后的隊列:[bbbb, cccc, dddd, eeee]