本文根据疯狂Java讲义(第3版)整理而成,感谢作者李刚老师
如果觉得内容不错的话,推荐大家买一本阅读,绝对物超所值哦
阅读本文前可以先看Java——Java集合概述 Java——Java集合List
四、Queue集合
Queue集合用于模拟队列这种数据结构,队列通常是先进先出的容器。头部保存在队列中存放时间最长的元素,尾部保存存放时间最短的元素。新元素插入到队列尾部,访问元素会返回队列头部的元素。
一、Queue接口
1.Queue接口定义了如下i几个方法:
- void add(Object e): 将指定元素加入到队列尾部。
- Object element(): 获取队列头部的元素,但是不删除该元素。
- boolean offer(Object e): 将指定元素加入到队列尾部。此方法优于add(Object e)。
- Object peek(): 获取队列头部的元素,但是不删除该元素。如果队列为空,返回null。
- Object poll(): 获取队列头部的元素,并删除该元素。如果队列为空,返回null。
- Object remove(): 获取队列头部的元素,并删除该元素。
2.Queue接口有一个PriorityQueue实现类。除此之外,Queue还有一个Deque接口,Deque代表一个双端队列,双端队列可以通知从两段来添加、删除元素,因此Deque的实现类即可当作队列使用,也可以当成栈使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。
二、PriorityQueue实现类
1.PriorityQueue是一个比较标准的队列实现类。但是它保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。优先级高的排在前面,优先级低的排在后面。因此当调用peek()或poll()方法取出队列中的元素时,并不是取出最先进入队列的元素,二是取出队列中优先级最高的元素。(从这一点看PriorityQueue已经违反了队列的基本规则:先进先出)
public PriorityQueueTest{
public static void main(String[] args){
PriorityQueue pq = new PriorityQueue();
pq.offer(6);
pq.offer(-3);
pq.offer(9);
pq.offer(20);
System.out.println(pq.poll());
System.out.println(pq.poll());
System.out.println(pq.poll());
System.out.println(pq.poll());
// 将会输出-3 6 9 20
}
}
所示代码中PriorityQueue没有给定排序方式,队列按自然排序升序方式保存元素(越小的元素优先级越高),输出时按从小到大输出。
2.PriorityQueue不允许插入null元素,因为它需要对队列元素进行排序。排序方式有两种:
- 自然排序:采用自然排序的PriorityQueue集合中的元素类型必须实现了ComParable接口,而且应该是同一个类的实例。(同TreeSet,参考Java——Java集合Set)
- 定制排序:创建PriorityQueue队列时,传入一个Comparator对象,该对象负责对队列中的所有元素进行排序。(同TreeSet)
三、Deque接口与ArrayDeque,LinkedList实现类
1.Deque接口是Queue接口的子接口,它代表一个双端队列,Deque接口里定义了一些双端队列的方法,这些方法允许从两端来操作队列的元素。方法如下,
- void addFirst(Object e):Java在线API中文文档 - 开源中国
- void addLast(Object e):
- Iterator descendingIterator():
- Object getFirst():
- Object getLast():
- boolean offerFirst(Object e):
- boolean offerLast(Object e):
- Object peekFirst():
- Object peekLast():
- Object pollFirst():
- Object pollLast():
- Object pop()(栈方法):
- void push(Object e)(栈方法):
- Object removeFirst():
- Object removeFirstOccurrence(Object o):
- Object removeLast():
- Object removeLastOccurrence(Object o):
2.ArrayDeque是Deque接口的一个典型的实现类:,它基于数组Object[]实现。创建Deque时可指定一个numElements参数(数组长度),如果不指定的话数组长度默认为16。当集合元素超过数组容量时,系统会自动分配一个更大容量的Object[]数组来存储集合元素。ArrayDeque既可以当作栈使用(避免使用Stack),也可以当作队列使用。
3.LinkedList是Deque接口的另一个实现类,可以当作双端队列使用,既可作为栈,也可作为队列。同时,它还实现了List接口,也是一个List集合。LinkedList是一个功能非常强大的集合类。
4.LinkedList与ArrayList、ArrayDeque的实现机制完全不同。ArrayList、ArrayDeque内以数组的形似来保存集合中的元素,因此随机访问时有较好的性能;而LinkedList内部以链表的形式来保存元素,因此随机分访问集合元素时性能较差,但在插入,删除元素时性能比较出色。需要指出的是,虽然Vector也是以数组的形式来存储集合元素的,但因为它实现了线程同步功能(而且实现机制也不好),所以各方面性能都比较差。
四、各种线性表的比较和分析
Java提供的List就是一个线性表接口,而ArrayList、LinkedList又是线性表的两种典型的实现;基于数组的线性表和基于链的线性表。Queue代表了队列,也是线性表。Deque代表了双端队列(既可作为队列使用,也可以作为栈使用),接下来对各种实现类的性能进行分析。
初学者可以无须理会ArrayList和LinkedList之间的性能差异,只需要知道LinkedList集合不仅提供了List的功能,还提供了双端队列、栈的功能就行。但对于一个成熟的Java程序员,在一些性能非常敏感的地方,可能需要慎重选择哪个List实现。
一般来说,由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能都比较好;而内部以链表作为底层实现的集合在执行插入、删除操作时有较好的性能。但总体来说,ArrayList的性能比LinkedList的性能要好,因此大部分时候都应该考虑使用ArrayList。
关于使用List集合有如下建议。
如果需要遍历List集合元素,对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,则应该采用迭代器(Iterator)来遍历集合元素。
如果需要经常执行插入、删除操作来改变包含大量数据的List集合的大小,可考虑使用LinkedList集合。使用ArrayList、Vector集合可能需要经常重新分配内部数组的大小,效果可能较差。
如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合。
——李刚老师
1.疯狂Java讲义(第3版)
2.Java在线API中文文档 - 开源中国