数据结构之Queue入门与详解

简介: 数据结构之Queue入门与详解

Queue用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。


【1】Queue的几个接口

Queue是设计用于在处理前保存元素的集合。


除了基本的 java.util.Collection 操作外,队列还提供其他插入、提取和检查操作。这些方法都有两种形式:一种是在操作失败时抛出异常,另一种是返回特殊值(取决于操作,null或 false)。设计了插入操作的后一种形式专门用于容量受限的队列。在大多数实现中,插入操作不会失败也就是允许元素为null(但是不鼓励插入null)。


队列通常(但不一定)以FIFO(先进先出)方式对元素进行排序。例外情况包括优先级队列,该队列根据提供的比较器或元素的自然排序对元素排序,以及后进先出LIFO 队列(或堆栈),该队列对元素进行后进先出排序。


无论使用何种排序,队列的 头 都是通过调用 remove 或 poll移除的元素。在FIFO队列中,所有新元素都插入队列的尾部。其他类型的队列可以使用不同的放置规则。每个Queue实现都必须指定其排序属性。


Queue 接口没有定义在并发编程中常见的 阻塞队列方法。这些方法(等待元素出现或空间可用)在java.util.concurrent.BlockingQueue接口中定义,该接口扩展了Queue 接口。


① Queue([kjuː])

如下所示,Queue提供了关于元素增加、移除以及获取的方法。

public interface Queue<E> extends Collection<E> {
     //如果可以在不违反容量限制的情况下立即将指定元素插入此队列,则在成功时返回 true,
     //如果当前没有可用空间,则抛出 IllegalStateException。
    boolean add(E e);
     //如果可以在不违反容量限制的情况下立即将指定的元素插入此队列中。
//当使用容量受限队列时,此方法通常优于{add},后者仅通过抛出异常表示插入失败
    boolean offer(E e);
     //检测并移除队列的头结点,如果队列为空,抛出异常NoSuchElementException 
    E remove();
     // 检测并移除队列的头结点,如果队列为空,返回null
    E poll();
     //获取队列的头结点,如果队列为空抛出异常NoSuchElementException 
    E element();
//获取队列的头结点,如果队列为空返回null
    E peek();
}


常见实现有ConcurrentLinkedDeque。

② Deque


Deque继承自Queue接口,Deque接口代表一个"双端队列",双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用。


deque是“双端队列double ended queue”的缩写,通常发音为“deck”。


大多数Deque实现对它们可能包含的元素数量没有固定限制,但该接口支持容量受限的Deque以及没有固定大小限制的Deque。


该接口定义了访问deque两端元素的方法。提供了插入、移除和检查元素的方法。这些方法都有两种形式:一种是在操作失败时抛出异常,另一种是返回特殊值(取决于操作, null或 false)。后一种形式的插入操作专门设计用于容量受限的.Deque实现;在大多数实现中,插入操作不会失败。


下表总结了上述十二种方法:

此接口扩展了 Queue 接口。当deque用作队列时,会产生FIFO(先进先出First-In-First-Out)行为。元素添加在deque的末尾,并从开头移除。从 Queue 接口继承的方法完全等同于Deque 方法,如下表所示:

Queue Method Equivalent Deque Method
java.util.Queue#add add(e) addLast(e)
java.util.Queue#offer offer(e) offerLast(e)
java.util.Queue#remove remove() removeFirst()
java.util.Queue#poll poll() pollFirst()
java.util.Queue#element element() getFirst()
java.util.Queue#peek peek() peekFirst()


DEQUE还可以用作后进先出LIFO (Last-In-First-Out)堆栈。此接口应优先于遗留 Stack 类使用。

当deque用作堆栈时,元素从deque的开头被推送和弹出。堆栈方法完全等同于 Deque 方法,如下表所示:

Stack Method Equivalent Deque Method
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

请注意,当deque用作队列或堆栈时,peek 方法同样有效。在这两种情况下,元素都是从deque的开头检测的。


与 List 接口不同,此接口没有提供对元素索引访问的支持。也就是说你不能使用类似get(index)来获取目标位置的元素。


虽然 很多 Deque 的实现并不严格要求禁止插入空元素,但是不鼓励用户插入null元素。这是因为 null 被各种方法用作特殊返回值,以指示deque为空。


Deque的实现有LinkedList、ArrayDeque等

③ BlockingQueue


其是java.util.concurrent(并发)包下的一个接口,BlockingQueue继承自Queue。它还支持在检索元素时等待队列变为非空,在存储元素时等待队列中空间变为可用的操作。


BlockingQueue}方法有四种形式,有不同的处理操作的方法,这些操作不能立即满足,但可能在将来的某个时候满足。


第一种抛出异常,第二种返回一个特殊值(取决于操作, null 或 false ),第三种无限期阻塞当前线程,直到操作成功,第四种是在给定时间前阻塞,之后就放弃。下表总结了这些方法:



Throws exception
Special value Blocks Times out
Insert add(e) offer(e) put(e) offer(e, time, unit)
Remove remove() poll() take() poll(time, unit)
Examine element() peek() not applicable not applicable

BlockingQueue不接受 null元素。实现类在 add、 put 或 offer null元素将会时抛出 NullPointerException。null 用作指示 poll 操作失败的敏感值。


BlockingQueue可能是容量受限的。在任何给定时间,它可能具有remainingCapacity(剩余容量),超过该值 put 操作就不能无阻塞地添加额外的元素。


没有任何内在容量约束的BlockingQueue始终报告 Integer.MAX_VALUE 的剩余容量。


BlockingQueue实现主要用于生产者-消费者队列,但还支持java.util.Collection接口。因此,例如,可以使用 remove(x)从队列中移除任意元素。但是,此类操作通常不会非常有效地执行,仅用于偶尔使用,例如取消排队消息时。


BlockingQueue 实现是线程安全的。所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果。但是,除非在实现中另有规定,否则Collection的批量操作addAll、 containsAll、 retainAll 和 removeAll 不一定是原子执行的。因此,例如,addAll©可能在只添加了集合.c 中的一些元素后失败(引发异常)。


BlockingQueue 本质上不支持任何类型的close or shutdown操作来标明不再添加元素。这些特性的需要和使用往往取决于实现。例如,一种常见的策略是生产商插入特殊的end-of-stream或poison对象,当消费者使用这些对象时,会相应地进行处理。


使用示例,基于典型的生产者-消费者场景。请注意, BlockingQueue 可以安全地用于多个生产者和多个消费者。

class Producer implements Runnable {
  private final BlockingQueue queue;
  Producer(BlockingQueue q) { queue = q; }
  public void run() {
    try {
      while (true) { queue.put(produce()); }
    } catch (InterruptedException ex) { ... handle ...}
  }
  Object produce() { ... }
}
class Consumer implements Runnable {
  private final BlockingQueue queue;
  Consumer(BlockingQueue q) { queue = q; }
  public void run() {
    try {
      while (true) { consume(queue.take()); }
    } catch (InterruptedException ex) { ... handle ...}
  }
  void consume(Object x) { ... }
}
class Setup {
  void main() {
    BlockingQueue q = new SomeQueueImplementation();
    Producer p = new Producer(q);
    Consumer c1 = new Consumer(q);
    Consumer c2 = new Consumer(q);
    new Thread(p).start();
    new Thread(c1).start();
    new Thread(c2).start();
  }
}}

内存一致性影响:与其他并发集合一样,在将对象放入 BlockingQueue的线程中的操作happen-before在另一个线程的 对BlockingQueue中访问或移除该元素的操作之前。


常见实现类有SynchronousQueue、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue等。

【3】 ArrayDeque


是一个基于数组的双端队列,和ArrayList类似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重新分配一个Object[]数组来存储集合元素。


实例代码如下:

import java.util.*;public class ArrayDequeTest
{    public static void main(String[] args) 
   {
       ArrayDeque stack = new ArrayDeque();        
       //依次将三个元素push入"栈"
       stack.push("Java");
       stack.push("轻量级Java EE企业应用实战");
       stack.push("Android");       
        //输出:[Java, 轻量级Java EE企业应用实战 , Android]
       System.out.println(stack);        
       //访问第一个元素,但并不将其pop出"栈",输出:Android
       System.out.println(stack.peek());        
       //依然输出:[Java, 轻量级Java EE企业应用实战 , Android]
       System.out.println(stack);       
        //pop出第一个元素,输出:Android
       System.out.println(stack.pop());       
        //输出:[Java, 轻量级Java EE企业应用实战]
       System.out.println(stack);
   }
}


目录
相关文章
|
2月前
|
应用服务中间件 nginx C语言
Nginx入门 -- 基本数据结构中之ngx_str_t,ngx_array_t
这两种数据结构是Nginx自定义数据类型的例子,它们证明了Nginx设计者在构建一个为高并发和高性能优化的web服务器时的精确和高效。理解这些数据结构是深入学习Nginx内部机制的基础,同时也是扩展和开发Nginx模块不可或缺的一部分知识。
28 1
|
3月前
|
算法 数据挖掘 计算机视觉
Python并查集实战宝典:从入门到精通,让你的数据结构技能无懈可击!
【7月更文挑战第17天】并查集,如同瑞士军刀,是解决元素分组问题的利器,应用于好友关系、像素聚类、碰撞检测和连通性分析等场景。本文从基础到实战,介绍并查集的初始化、查找与路径压缩、按秩合并,以及在Kruskal算法中的应用。通过并查集,实现高效动态集合操作,对比哈希表和平衡树,其在合并与查找上的性能尤为突出。学习并查集,提升算法解决复杂问题的能力。
66 5
|
2月前
|
存储 算法 调度
10种 Python数据结构,从入门到精通
10种 Python数据结构,从入门到精通
31 0
|
4月前
|
存储 算法 Java
老程序员分享:java之数据结构【入门篇】
老程序员分享:java之数据结构【入门篇】
31 0
|
4月前
|
存储 安全 Java
Go语言入门之路——数据结构
Go语言入门之路——数据结构
120 0
|
4月前
|
C语言 C++
【数据结构】C语言实现:栈(Stack)与队列(Queue)
【数据结构】C语言实现:栈(Stack)与队列(Queue)
|
4月前
|
机器学习/深度学习 算法
数据结构入门 时间 空间复杂度解析
数据结构入门 时间 空间复杂度解析
28 0
|
5月前
|
存储 NoSQL Java
Redis入门到通关之数据结构解析-Dict
Redis入门到通关之数据结构解析-Dict
71 2
|
5月前
|
存储 NoSQL 算法
Redis入门到通关之Redis数据结构-Hash篇
Redis入门到通关之Redis数据结构-Hash篇
62 1
|
5月前
|
存储 NoSQL Redis
Redis入门到通关之Redis数据结构-Set篇
Redis入门到通关之Redis数据结构-Set篇
66 1