集合框架之Queue接口

来源:互联网 发布:mysql创始人 编辑:程序博客网 时间:2024/05/02 00:47

Queue接口

在处理元素前用于保存元素的 collection。除了基本的 Collection 操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。

操作

抛出异常

返回特殊值

插入

add(e)

offer(e)

移除

remove()

poll()

检查

element()

peek()

队列通常(但并非一定)以FIFO(先进先出)的方式排序各个元素。不过优先级队列和 LIFO 队列(或堆栈)例外,前者根据提供的比较器或元素的自然顺序对元素进行排序,后者按 LIFO(后进先出)的方式对元素进行排序。无论使用哪种排序方式,队列的头都是调用 remove() 或 poll() 所移除的元素。在 FIFO 队列中,所有的新元素都插入队列的末尾。其他种类的队列可能使用不同的元素放置规则。每个 Queue 实现必须指定其顺序属性。

如果可能,offer方法可插入一个元素,否则返回 false。这与 Collection.add 方法不同,该方法只能通过抛出未经检查的异常使添加元素失败。offer 方法设计用于正常的失败情况,而不是出现异常的情况,例如在容量固定(有界)的队列中。

remove() 和 poll() 方法可移除和返回队列的头。到底从队列中移除哪个元素是队列排序策略的功能,而该策略在各种实现中是不同的。remove() 和 poll() 方法仅在队列为空时其行为有所不同:remove() 方法抛出一个异常,而 poll() 方法则返回 null。

element() 和 peek() 返回,但不移除,队列的头。

Queue 接口并未定义阻塞队列的方法,而这在并发编程中是很常见的。BlockingQueue 接口定义了那些等待元素出现或等待队列中有可用空间的方法,这些方法扩展了此接口。

Queue 实现通常不允许插入null 元素,尽管某些实现(如LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。

Queue接口方法列表如下:

 boolean add(E e)

          将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。

 E element()

          获取,但是不移除此队列的头。

 boolean offer(E e)

          将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。

 E peek()

          获取但不移除此队列的头;如果此队列为空,则返回 null。

 E poll()

          获取并移除此队列的头,如果此队列为空,则返回 null。

 E remove()

          获取并移除此队列的头。

在List接口中已经说过,LinkedList也实现了Queue接口,可作为普通队列的容器,在这里不再赘述


PriorityQueue

一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)。

此队列的头是按指定排序方式确定的最小元素。如果多个元素都是最小值,则头是其中一个元素——选择方法是任意的。队列获取操作 poll、remove、peek 和 element 访问处于队列头的元素。

优先级队列是无界的,但是有一个内部容量(默认为11),控制着用于存储队列元素的数组大小。它通常至少等于队列的大小。随着不断向优先级队列添加元素,其容量会自动增加。无需指定容量增加策略的细节。

此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选方法。方法iterator() 中提供的迭代器不保证以任何特定的顺序遍历优先级队列中的元素。如果需要按顺序遍历,请考虑使用 Arrays.sort(pq.toArray())。

扩展的构造方法:

PriorityQueue(int initialCapacity)

          使用指定的初始容量创建一个PriorityQueue,并根据其自然顺序对元素进行排序。

PriorityQueue(int initialCapacity, Comparator<? super E> comparator)

          使用指定的初始容量创建一个PriorityQueue,并根据指定的比较器对元素进行排序。

PriorityQueue(PriorityQueue<?extends E> c)

          创建包含指定优先级队列元素的 PriorityQueue。

PriorityQueue(SortedSet<?extends E> c)

         创建包含指定有序 set 元素的 PriorityQueue。

 

ArrayBlockingQueue

一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。

这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来达到这种公平性的:即等待时间最长的线程会先操作)。

使用阻塞队列可以很方便的实现生产者-消费者模型:

 

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue; public class Test {         public static void main(String[] args) {             //定义一个容量为5的阻塞队列             BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);                   Producer p = new Producer(queue);             Consumer c = new Consumer(queue);                         new Thread(p).start();             new Thread(c).start();     } } //生产者class Producer implements Runnable{       private final BlockingQueue<Integer> queue;           public Producer(BlockingQueue<Integer> q){             this.queue = q;      }           public void run() {                         while(true){                                       try {                           queue.put(1);  //生产者不断地往队列添加产品,当队列已满的时候,该线程会被阻塞                           System.out.println("生产者线程:+1,队列长度:"+queue.size());                    } catch (InterruptedException e) {                           e.printStackTrace();                    }                                }                  }     } //消费者class Consumer implements Runnable{       private final BlockingQueue<Integer> queue;           public Consumer(BlockingQueue<Integer> q){             this.queue = q;      }           public void run() {                         while(true){                                       try {                           queue.take();  //消费者不断消耗产品,当队列已空的时候,该线程会被阻塞                           System.out.println("消费者线程:-1,队列长度:"+queue.size());                    } catch (InterruptedException e) {                           e.printStackTrace();                    }                                }                  }     }
 

除了基于数组的ArrayBlockingQueue,优先级阻塞队列PriorityBlockingQueue,以及适用于并发环境的ConcurrentLinkedQueue


Deque接口

Deque接口是Queue的子接口。

Deque支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是专为使用有容量限制的 Deque 实现设计的;在大多数实现中,插入操作不能失败。

下表总结了上述 12 种方法:

操作

第一个元素(队头)

最后一个元素(队尾)

抛出异常

返回特殊值

抛出异常

返回特殊值

插入

addFirst(e)

offerFirst(e)

addLast(e)

offerLast(e)

移除

removeFirst()

pollFirst()

removeLast()

pollLast()

检查

getFirst()

peekFirst()

getLast()

peekLast()

此接口扩展了 Queue 接口。在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:

Queue 方法

等效 Deque 方法

add(e)

addLast(e)

offer(e)

offerLast(e)

remove()

removeFirst()

poll()

pollFirst()

element()

getFirst()

peek()

peekFirst()

双端队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:

堆栈方法

等效 Deque 方法

push(e)

addFirst(e)

pop()

removeFirst()

peek()

peekFirst()

注意,在将双端队列用作队列或堆栈时,peek方法同样正常工作;无论哪种情况下,都从双端队列的开头抽取元素。

此接口提供了两种移除内部元素的方法:removeFirstOccurrence和 removeLastOccurrence。

与 List 接口不同,此接口不支持通过索引访问元素

虽然 Deque 实现没有严格要求禁止插入null 元素,但建议最好这样做。建议任何事实上允许 null 元素的 Deque 实现用户最好不要利用插入 null 的功能。这是因为各种方法会将 null 用作特殊的返回值来指示双端队列为空。

LinkedList实现了Deque接口,再次不再赘述


ArrayDeque

Deque 接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止null 元素。此类很可能在用作堆栈时快于 Stack,在用作队列时快于LinkedList。

大多数 ArrayDeque 操作以摊销的固定时间运行。异常包括 remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove() 以及批量操作,它们均以线性时间运行。

此类及其迭代器实现 Collection 和 Iterator 接口的所有可选方法。

扩展的构造方法:

ArrayDeque(int numElements)

          构造一个初始容量能够容纳指定数量的元素的空数组双端队列。


LinkedBlockingDeque

一个基于已链接节点的、任选范围的阻塞双端队列。

可选的容量范围构造方法参数是一种防止过度膨胀的方式。如果未指定容量,那么容量将等于 Integer.MAX_VALUE。只要插入元素不会使双端队列超出容量,每次插入后都将动态地创建链接节点。

大多数操作都以固定时间运行(不计阻塞消耗的时间)。异常包括 remove、removeFirstOccurrence、removeLastOccurrence、contains、iterator.remove() 以及批量操作,它们均以线性时间运行。


7 5
原创粉丝点击