【Java数据结构及算法实战】系列007:Java队列01——Queue概述

简介: 列与栈类似,也是一种运算受限的线性表。队列则被限定在表尾进行插入、在表头进行删除,这种数据结构,实现了FIFO(First In First Out,先进先出)或者是LILO(Last In Last Out,后进后出)的方式工作。

队列与栈类似,也是一种运算受限的线性表。队列则被限定在表尾进行插入、在表头进行删除,这种数据结构,实现了FIFO(First In First Out,先进先出)或者是LILO(Last In Last Out,后进后出)的方式工作。

 

 

 

下图很形象将队列比作是实现生活中的排队。排在队列前面的总是会最先得到处理,而排在队列后面的总是最后得到处理。

 

 

 

 

 

 

1.   队列的基本概念

 

 

 

 

 

在对队列有了基本的认识之后,我们再来看下队列的基本概念。

 

 

 

进行插入操作这一端被称为队尾(tail),相对地,把另一端称为队首(head)。向一个队列插入新元素又称作入队(enqueue)。它是把新元素放到队尾元素的之后,使之成为新的队尾元素。从一个队列中删除元素又称作出队(dequeue)。它是把队首元素删除掉,使其相邻的元素成为新的队首元素。

 

 

 

 

 

 

 

2.   Java对于队列的支持

 

 

 

在Java中,提供了java.util.Queue<E>接口以支持队列。根据实现不同,队列又可以分为以下几种场景。

 

 

 

 

 

2.1.     是否阻塞

 

阻塞是指当队列空时,消费资源是否阻塞;当队列(有界队列)满时,插入数据是否阻塞。

 

 

 

Java提供了java.util.concurrent.BlockingQueue<E>接口以表示阻塞队列。常见的阻塞队列有:

 

 

 

l  ArrayBlockingQueue

 

l  LinkedBlockingQueue

 

l  DelayQueue

 

l  PriorityBlockingQueue

 

l  SynchronousQueue

 

l  LinkedBlockingDeque

 

l  等等

 

2.2.     单端还是双端

 

单端还是双端,相比于单端队列,双端队列可以从队列的两头分别进行入队和出队操作。

 

 

 

Java提供了java.util.Deque<E>接口以表示双端队列。常见的双端队列有:

 

 

 

l  ArrayDeque

 

l  ConcurrentLinkedDeque

 

l  LinkedList

 

l  LinkedBlockingDeque

 

l  等等

 

 

 

2.3.     是否有界

 

 

 

判断一个队列是否有界的依据是,队列在初始化时是否设置了容量。以ArrayBlockingQueue为例,ArrayBlockingQueue在初始化时,强制要求以容量作为构造函数的参数之一,这便是有界的。LinkedList是无界的,队列的长度会随着入队的元素的增多而不断增长。

 

 

 

LinkedBlockingQueue比较特殊,在初始化时可以指定容量也可以不指定容量。当初始化LinkedBlockingQueue指定容量时,是有界队列;当初始化LinkedBlockingQueue未指定容量时,其内部会以Integer.MAX_VALUE值作为容量。当然,因为Integer.MAX_VALUE值非常大,近似无限大,因此LinkedBlockingQueue未指定容量时也可以近似认为是无界队列。

 

 

 

在实际项目中,推荐优先选择使用有界队列。因为无界队列可能产生OOM(Out Of Memory,内存溢出)问题,OOM对系统是致命的。

 

 

 

2.4.     内部数据结构

 

队列的内部数据结构对使用者而言应该是透明的,使用者关注内部数据结构。主要是关注数据结构对业务运行的影响。队列的内部数据结构有数组、链表、堆等,不同数据结构会影响GC、CPU缓存,大长时间运行后,使用链表会产生大量的碎片,对GC造成很大的压力;内存连续的空间,也更有利于CPU缓存的发挥。

 

 

 

根据存储结构的不同,队列还分为以下几种:

 

 

 

l  用顺序表实现的队列称为顺序队列

 

l  用链表实现的队列称为链队列

 

 

 

2.5.     是否无锁

 

加锁的开锁是巨大的,在高并发场景下,无锁的性能一般有锁的数倍。

 

2.6.     其他特殊功能

 

有很多队列都有特殊的功能,来满足特殊的场景。如SynchronousQueue没有缓存用于handoff场景(下文介绍);PriorityQueue、PriorityBlockingQueue支持按优先级入队;DelayedQueue支持延时出队;Disruptor除具有超强的性能外,还支持多消费者复杂的消费模式。

 

 

 

 

 

基于上面的场景,整理了下表最常用的10个队列的实现。

 

 

 

表1-1 最常用的10个队列的实现

                                                                                                                                                                                                                                                                                               

   

队列

   
   

是否阻塞

   
   

单端还是双端

   
   

是否有界

   
   

内部数据结构

   
   

是否无锁

   
   

特殊功能

   
 

ArrayBlockingQueue

 
 

阻塞

 
 

单端

 
 

 
 

数组

 
 

有锁

 
 

NA

 
 

LinkedBlockingQueue

 
 

阻塞

 
 

单端

 
 

 
 

链表

 
 

有锁

 
 

NA

 
 

SynchronousQueue

 
 

阻塞

 
 

单端

 
 

 
 

单一元素

 
 

有锁

 
 

支持HandOff功能;

 
 

LinkedTransferQueue

 
 

阻塞

 
 

单端

 
 

 
 

链表

 
 

有锁

 
 

功能为LinkedBlockingQueue与SynchronousQueue的超集;

 
 

PriorityBlockingQueue

 
 

阻塞

 
 

单端

 
 

 
 

 
 

有锁

 
 

支持优先级队列;

 
 

DelayQueue

 
 

阻塞

 
 

单端

 
 

 
 

 
 

有锁

 
 

支持延时出队;

 
 

LinkedBlockingDeuqe

 
 

阻塞

 
 

双端

 
 

 
 

链表

 
 

有锁

 
 

NA

 
 

ConcurrentLinkedQueue

 
 

非阻塞

 
 

单端

 
 

 
 

链表

 
 

有锁

 
 

NA

 
 

ConcurrentLinkedDeque

 
 

非阻塞

 
 

双端

 
 

 
 

链表

 
 

有锁

 
 

NA

 
 

Disruptor

 
 

阻塞

 
 

单端

 
 

 
 

环形数组

 
 

无锁

 
 

超强性能,支持多种生产者消费者模式;

 

 

参考引用

 

目录
相关文章
|
2月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
91 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
71 2
|
15天前
|
Java 程序员
Java基础却常被忽略:全面讲解this的实战技巧!
小米,29岁程序员,分享Java中`this`关键字的用法。`this`代表当前对象引用,用于区分成员变量与局部变量、构造方法间调用、支持链式调用及作为参数传递。文章还探讨了`this`在静态方法和匿名内部类中的使用误区,并提供了练习题。
18 1
|
26天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
48 6
|
25天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
2月前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
55 1
|
2月前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
141 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
2月前
|
算法 Java Linux
java制作海报一:java使用Graphics2D 在图片上写字,文字换行算法详解
这篇文章介绍了如何在Java中使用Graphics2D在图片上绘制文字,并实现自动换行的功能。
141 0
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
166 9