Java集合面试之Queue篇 (qq.com)
1、队列是什么?
队列是常用数据结构之一。是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,故为先进先出(FIFO,first in first out)线性表。
和栈一样,队列是一种操作受限制的线性表。
2、队列的分类?
Queue 大体可分为以下几类:
双端队列:双端队列Deque是 Queue 的子类也是 Queue 的补充类,头部和尾部都支持元素插入和获取。
阻塞队列:阻塞队列指的是在元素操作时(添加或删除),如果没有成功,会阻塞等待执行。例如,当添加元素时,如果队列元素已满,队列会阻塞等待直到有空位时再插入。有BlockingQueue、LinkedBlockingQueue、ArrayBlockingQueue和DelayQueue。
非阻塞队列:非阻塞队列会直接返回操作的结果。双端队列也属于非阻塞队列。如ConcurrentLinkedQueue。
优先级队列:PriorityQueue 一个基于优先级堆的无界优先级队列。
并发队列:有阻塞队列和非阻塞队列,前者使用锁实现,后者则使用CAS非阻塞算法实现,使用非阻塞队列一般性能比较好。
3、Java中队列有哪些?有什么区别?
(1)内置的非阻塞队列:PriorityQueue 和 ConcurrentLinkedQueue;
-
PriorityQueue 类实质上维护了一个有序列表。加入到 Queue 中的元素根据它们的天然排序(通过
java.util.Comparable
实现)或者根据传递给构造函数的java.util.Comparator
实现来定位。 -
ConcurrentLinkedQueue 是基于链接节点的、线程安全的队列。并发访问不需要同步。因为它在队列的尾部添加元素并从头部删除它们,所以只要不需要知道队列的大小,对公共集合的共享访问做得很好。收集关于队列大小的信息会很慢,需要遍历队列。
(2)实现阻塞接口的:java.util.concurrent 中加入了 BlockingQueue
接口和五个阻塞队列类。它不是立即从队列中添加或者删除元素,没有空间入或者没有元素出时,线程执行操作阻塞。五个队列所提供的各有不同:
-
ArrayBlockingQueue :一个由数组支持的有界队列。
-
LinkedBlockingQueue :一个由链接节点支持的可选有界队列。
-
PriorityBlockingQueue :一个由优先级堆支持的无界优先级队列。
-
DelayQueue :一个由优先级堆支持的、基于时间的调度队列。
-
SynchronousQueue :一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。
4、有界队列和无界队列的区别?
有界队列:有固定大小的队列叫做有界队列,比如:new ArrayBlockingQueue(6),6 就是队列的大小。
无界队列:指的是没有设置固定大小的队列,这些队列的特点是可以直接入列,直到溢出。它们并不是真的无界,它们最大值通常为 Integer.MAXVALUE
,只是平常很少能用到这么大的容量(超过 Integer.MAXVALUE),因此从使用者的体验上,就相当于 无界。
5、BlockingQueue是什么?
- 在进行检索或移除一个元素的时候,它会等待队列变为非空;
- 当添加元素时,它会等待队列中的可用空间。
BlockingQueue接口
属于集合框架,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。常见实现类有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。
6、ArrayBlockingQueue 与 LinkedBlockingQueue 的区别
-
队列中锁的实现不同,ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁;LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是
putLock
,消费是takeLock
。 -
在生产或消费时操作不同:ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的;LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为
Node<E>
进行插入或移除,会影响性能。 -
队列大小初始化方式不同:ArrayBlockingQueue实现的队列中必须指定队列的大小;LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是
Integer.MAX_VALUE
。 -
数据结构不同:ArrayBlockingQueue 数据存储容器是采用数组存储的;而 LinkedBlockingQueue 采用的是 Node 节点存储的。
注意:
1、在使用LinkedBlockingQueue时,若用默认大小且当生产速度大于消费速度时候,有可能会内存溢出。
2、在使用ArrayBlockingQueue和LinkedBlockingQueue分别对一百万个简单字符做入队操作时;LinkedBlockingQueue的消耗是ArrayBlockingQueue消耗的10倍左右。
7、在 Queue 中 poll()和 remove()有什么区别?
相同点:都是返回第一个元素,并在队列中删除返回的对象。
不同点:如果没有元素 poll()会返回 null
,而 remove()会直接抛出 NoSuchElementException
异常。
8、Stack和Queue的区别?
(1)栈和队列两者都被用来预存储数据。
(2)队列允许先进先出(FIFO)检索元素,但并非总是这样。Deque
接口允许从两端检索元素。栈是后进先出(LIFO)进行检索。
(3)Stack是一个扩展自Vector
的类,而Queue
是一个接口。