一、队列(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]