Java 队列介绍

98 阅读6分钟

队列(Queue)是一种常见的线程数据结构,它按照先进先出(FIFO)的原则管理元素。 根据队列的特性

  • 添加元素时只能添加到队列尾部
  • 删除数据时,只能删除头部
  • 获取数据时只能取头部

队列

普通队列

除了基本的集合操作外,队列还提供插入,删除和检查操作。这些方法中的每一种都以两种形式存在:

  • 条件不满足时(插入时队列空间不足,删除时队列数据为空)则会引发异常,
  • 条件不满足时(插入时队列空间不足,删除时队列数据为空),将返回特殊值(取决于操作 null或false)。

插入操作的后一种形式专为与容量限制的队列实现而设计。在大多数实现中,插入操作不会失败。

抛异常返回特殊值
插入add(e)offer(e)
删除remove()poll()
取值element()peek()

java Queue 常见实现

  • java.util.PriorityQueue 是 Java 中的一种优先级队列实现,它是基于堆数据结构实现的。优先级队列用于按照元素的优先级进行排序和获取,而不是按照插入顺序。元素在插入队列时会按照优先级顺序排列,允许高优先级元素优先被获取。

  • ConcurrentLinkedQueue 是 Java 中的一种线程安全的队列实现,专门设计用于多线程并发环境下的操作,基于链表数据结构的无界队列。与传统的队列不同,ConcurrentLinkedQueue 基于无锁算法实现,允许多个线程同时进行插入和移除操作,而不会引发竞态条件或死锁。

阻塞队列

BlockingQueue 它继承了Queue 插入,删除和取值操作除了普通队列的两种形式外,还有两种操作形式

  • 条件不满足时(插入时队列空间不足,删除时队列数据为空),进行无限期阻塞
  • 条件不满足时(插入时队列空间不足,删除时队列数据为空),阻塞一定的时长,返回特殊值(取决于操作 null或false)。
抛异常返回特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e, time, unit)
删除remove()poll()take()poll(time, unit)
取值element()peek()--

java BlockingQueue 常见实现

  • ArrayBlockingQueuejava.util.concurrent.ArrayBlockingQueue 是一个基于数组的阻塞队列实现。它具有固定的容量,需要在创建时指定容量大小。并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。当队列为空时,尝试从中获取元素的操作会被阻塞,直到有元素可用。当队列已满时,尝试插入元素的操作也会被阻塞,直到有足够的空间可用。
  • LinkedBlockingQueuejava.util.concurrent.LinkedBlockingQueue 是一个基于链表的阻塞队列实现。它支持无界队列和有界队列,在创建 LinkedBlockingQueue 对象时如果不指定容量大小,则默认大小为 Integer.MAX_VALUE。当队列为空时,尝试从中获取元素的操作会被阻塞,直到有元素可用。当队列已满时,尝试插入元素的操作也会被阻塞,直到有足够的空间可用。
  • PriorityBlockingQueuejava.util.concurrent.PriorityBlockingQueue 是一个无界的优先级队列,它支持根据元素的自然排序或指定的比较器进行排序。尝试获取元素的操作会阻塞,直到队列中有元素可用。
  • DelayQueuejava.util.concurrent.DelayQueue 是一个基于PriorityQueue用于实现延迟任务调度的队列,DelayQueue中的元素实现了 Delayed 接口,只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。
  • SynchronousQueuejava.util.concurrent.SynchronousQueue 是一个没有存储元素的阻塞队列,它用于在线程之间传递数据,一个线程尝试插入元素时会被阻塞,直到另一个线程尝试获取相同的元素。

双端队列

双端队列(Double-ended Queue),通常缩写为Deque,它继承了Queue 不仅支持队列(FIFO)操作,还支持栈(LIFO)操作,因此可以在队列的两端(前端和后端)进行插入和移除操作操作元素。

普通双端队列

以下是Java提供的实现Deque的常用类

  • ArrayDequeArrayDeque 是一个基于数组实现的双端队列,容量是动态增长的,当需要时,它会自动扩展容量,因此不会出现队列满的情况。可以在队列的两端进行元素的插入和移除操作。
  • LinkedListLinkedList 也实现了Deque接口,并且是一个双向链表,因此可以在队列的两端进行元素的插入和移除操作,容量也是动态增长的。
  • ConcurrentLinkedDeque 是 Java 并发包 (java.util.concurrent) 中提供的一种线程安全的双端队列(Deque)实现,基于链表数据结构的无界队列。它允许在队列的两端(前端和后端)执行插入和移除操作,并且提供了线程安全的操作,适用于多线程并发环境
第一个元素(Head)最后一个元素(Tail)
抛异常特殊值 null或false抛异常特殊值 null或false
插入addFirst(e)offerFirst(e)addLast(e)offerLast(e)
删除removeFirst()pollFirst()removeLast()pollLast()
取值getFirst()peekFirst()getLast()peekLast()

队列(Queue)和双端队列(Deque)方法的等价关系

Queue 方法等价 Deque 方法
add(e)addLast(e)
offer(e)offerLast(e)
remove()removeFirst()
poll()pollFirst()
element()getFirst()
peek()peekFirst()

栈(Stack)和双端队列(Deque)方法的等价关系

Stack 方法等价 Deque 方法
push(e)addFirst(e)
pop()removeFirst()
peek()peekFirst()

阻塞双端队列

阻塞双端队列是一种特殊类型的双端队列,它在队列为空或已满时可以阻塞等待操作

抛异常特殊值阻塞超时
第一个元素 (Head)
插入addFirst(e)offerFirst(e)putFirst(e)offerFirst(e, time, unit)
删除removeFirst()pollFirst()takeFirst()pollFirst(time, unit)
取值getFirst()peekFirst()--
最后一个元素 (Tail)
插入addLast(e)offerLast(e)putLast(e)offerLast(e, time, unit)
删除removeLast()pollLast()takeLast()pollLast(time, unit)
取值getLast()peekLast()--

阻塞双端队列的常见实现:

LinkedBlockingDeque: 是一个基于链表的阻塞双端队列。容量大小固定,一旦创建无法更改

阻塞队列(BlockingQueue)和阻塞双端队列(BlockingDeque)方法的等价关系

BlockingQueue 方法等价于 BlockingDeque 方法
插入
add(e)addLast(e)
offer(e)offerLast(e)
put(e)putLast(e)
offer(e, time, unit)offerLast(e, time, unit)
删除
remove()removeFirst()
poll()pollFirst()
take()takeFirst()
poll(time, unit)pollFirst(time, unit)
取值
element()getFirst()
peek()peekFirst()