队列
1.基本概念
-
队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。
-
队列是逻辑结构,其物理结构可以是数组,也可以是链表。
-
队列的修改原则:队列的修改是依
先进先出(FIFO)的原则
进行的。新来的成员总是加入队尾(即不允许"加塞"),每次离开的成员总是队列头上的(不允许中途离队),即当前"最老的"成员离队。
常见的队列:
- ArrayList:ArrayList可以被用作队列,通过在列表末尾添加元素,并使用remove(0)方法从列表的开头删除元素。但是,由于在列表的开头删除元素会导致后续元素的移动,因此对于大量的插入和删除操作来说,ArrayList的性能可能不是最佳选择。
- LinkedList:LinkedList也可以用作队列。LinkedList实现了Queue接口,可以使用offer()方法在队列的末尾添加元素,使用poll()方法从队列的开头删除并返回元素。LinkedList对于插入和删除操作具有较好的性能,因为它使用了指针来链接元素,而不需要移动其他元素。
- ArrayBlockingQueue:ArrayBlockingQueue是一个有界阻塞队列,底层使用数组实现。它有一个固定的容量,并且在插入或删除元素时可能会阻塞线程,直到满足特定的条件。
- LinkedBlockingQueue:LinkedBlockingQueue是一个可选有界或无界的阻塞队列,底层使用链表实现。它具有类似于ArrayBlockingQueue的功能,但在内部实现上略有不同。
- PriorityBlockingQueue:PriorityBlockingQueue是一个支持优先级的无界阻塞队列。元素按照它们的优先级顺序被插入和删除。
- ConcurrentLinkedQueue:ConcurrentLinkedQueue是一个非阻塞无界队列,它适用于多线程环境。它使用链表实现,并且提供了高效的并发操作。
2.队列分类
按照实现方式:
(1)单向队列(Queue):只能在一端插入数据,在另一端删除数据。
(2)双向队列(Deque):每一端都可以进行插入数据和删除数据操作。
(3)优先级队列:优先级队列是比栈和队列更专用的数据结构,在优先级队列中,数据项按照关键字进行排序,关键字最小(或者最大)的数据项往往在队列的最前面,而数据项在插入的时候都会插入到合适的位置以确保队列的有序。
按照存储方式:
队列与栈一样是一种线性结构,因此以常见的线性表如数组、链表作为底层的数据结构,以数组、链表为底层数据结构构建队列。
(1)基于数组的顺序存储方式
缺点:通过出队列操作将数据弹出队列后,font指针之前的的空间就不能再次得到了,这样导致了大量的空间丢失。
为了解决上述问题,将普通数组换成循环数组,在循环数组中,末尾元素的下一个元素不是数组外,而是数组的头元素。 这样就能够再次使用front之前的存储空间了。
(2)基于链表的链式存储方式
说明:队首指针指向队首元素的前一个结点,即始终指向链表空的头结点,队尾指针指向队列当前队尾元素所在的结点。当队列为空时,队首指针与队尾指针均指向空的头结点
3.队列(Queue)方法
Queue是java中实现队列的接口,它总共只有6个方法,我们一般只用其中3个就可以了。Queue的实现类有LinkedList和PriorityQueue。最常用的实现类是LinkedList,因为LinkedList进行插入、删除操作效率较高。
队列的语法结构:
Queue<String> queue = new LinkedList<String>();
//将元素添加到队列末尾,若添加成功则返回true,该操作主要是给容量受限的队列设计的。
queue.offer();
// 从队首删除并返回该元素
queue.poll();
//返回队首元素,但是不删除
queue.peek();
双向队列(Deque),是Queue的一个子接口,双向队列是指该队列两端的元素既能入队 (offer) 也能出队 (poll), 如果将Deque限制为只能从一端入队和出队,则可实现栈的数据结构,对于栈而言,有入栈(push)和出栈(pop),遵循先进后出原则。
Deque<String> deque = new LinkedList<String>();
ArrayDeque是一种基于数组的双端队列实现,它同样实现了Queue接口,并且在尾部添加和移除元素的操作具有较低的时间复杂度。
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(1); // 入队
queue.offer(2);
int element = queue.poll(); // 出队
System.out.println(element); // 输出:1
PriorityQueue是一个基于优先级堆的无界优先级队列实现,它可以确保每次出队的元素都是队列中优先级最高的元素。需要注意的是,PriorityQueue会根据元素的自然顺序或者比较器来决定出队顺序。
Queue<Integer> queue = new PriorityQueue<>();
queue.offer(5); // 入队
queue.offer(3);
int element = queue.poll(); // 出队
System.out.println(element); // 输出:3
4.队列的应用场景
1.消息队列
用于实现系统间的异步通信,可以将消息发送到队列中,然后由消费者从队列中取出进行处理。
示例:使用RabbitMQ、Kafka等消息队列实现订单处理系统,将订单消息发送到队列中,后台系统从队列中消费并处理订单。
2.线程池任务调度
用于按照顺序执行任务,通常使用队列来存储待执行的任务。
示例:使用Java的Executor框架创建线程池,将需要执行的任务添加到线程池的任务队列中,线程池按照队列中的顺序依次执行任务。
3.缓存淘汰策略
用于限制缓存的大小,当缓存满时,通过队列中的先进先出规则来淘汰最早添加的元素。
示例:使用LRU(最近最少使用)缓存淘汰算法,将不经常访问的数据移出缓存,保留最近访问的数据在缓存中。
4.网络请求调度
用于处理请求队列,按照先到先处理的顺序处理请求,实现请求的有序处理。
示例:Web服务器接收到多个客户端请求时,将请求放入请求队列,然后按照队列中的顺序依次处理请求。
5.广度优先搜索(BFS)
用于解决图和树等数据结构的搜索问题,通过队列来实现搜索的层级遍历。
示例:在无权图中找到两个节点之间的最短路径,使用广度优先搜索算法,使用队列来实现节点的层级遍历。