目标:了解JAVA的Queue队列
1. Queue队列
Queue<T>
:Queue接口不属于Java并发包下面的类,它是java.util下面的一个接口,虽然这个接口跟并发没有直接关系,但是它抽象和定义了在Java里面队列的通用方法;Queue队列接口在实现上又继承了Collection接口,而Collection接口是继承了Iterable接口,所以继承了这两个接口的定义的方法功能。- Queue接口拥有的直接子类如下:
AbstractQueue
ArrayBlockingQueue
ArrayDeque
ConcurrentLinkedDeque
ConcurrentLinkedQueue
DelayQueue
LinkedBlockingDeque
LinkedBlockingQueue
LinkedList
LinkedTransferQueue
PriorityBlockingQueue
PriorityQueue
SynchronousQueue
- Java队列queue的操作:
Java队列queue的操作 | ||
---|---|---|
add | 增加一个元素 | 如果队列已满,抛出IIIegaISlabEepeplian异常 |
remove | 移除并返回队列头部的元素 | 如果队列为空,抛出NoSuchElementException异常 |
element | 返回队列头部的元素 | 如果队列为空,抛出一个NoSuchElementException异常 |
offer | 添加一个元素并返回true | 如果队列已满,返回false |
poll | 移除并返回队列头部的元素 | 如果队列为空,返回null |
peek | 返回队列头部的元素 | 如果队列为空,返回null |
put | 添加一个元素 | 如果队列已满,阻塞 |
take | 移除并返回队列头部的元素 | 如果队列空,阻塞 |
- Queue接口中定义了几种方法:
void add(Object e); //将指定元素加入此队列的尾部。
Object element(); //获取队列头部的元素,但是不删除该元素。
boolean offer(Object e); //将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Object e)方法更好。
Object peek(); //获取队列头部的元素,但是不删除该元素,如果此队列为空,则返回null。
Object poll(); //获取队列头部的元素,并删除该元素,如果此队列为空,则返回null。
Object remove(); //获取队列头部的元素,并删除该元素
- Queue队列的设计:
1)队列的实现一般不允许插入null值,尽管一些队列实现了,如LinkedList就没有禁止,即使LinkedList可以插入null值,但在实际使用的时候也不应向队列中插入null值,因为null值通常用于poll方法表示当前队列没有元素了。如果允许插入null值,方法会产生歧义;
2)队列的实现通常不需要重写该类的equals和hashCode方法,代替使用Object类默认的定义,因为在队列里面一样的元素可能拥有不同的排序属性。
- Queue队列和Stack栈方法区分:
poll:queue的方法,获取并移除此队列的头,如果队列为空,返回null;
pop:stack的方法,移除堆栈顶部的对象,并作为此函数的值返回该对象;
peek:queue&stack的方法,获取队列/栈头部/栈顶的元素,但不删除该元素。
2. Queue队列的直接子类:
1)LinkedList:(链表)多功能实现的类
- 是List接口的集合,可以根据索引值随意访问list中的元素,还实现了Deque接口(Queue的子接口,代表双向队列),Deque中也定义了一些双向操作队列的方法。还可以用作栈,因为有
pop()
和push()
两个方法。 - 如果需要遍历List集合元素,对于ArrayList、Vector集合,应使用 随机访问方法
get()
来遍历集合元素,对于LinkedList集合,应使用Iterator()
遍历集合元素。 - 如果需要经常执行插入、删除操作来改变List集合的大小,应使用LinkedList集合,而不是ArrayList。使用ArrayList、Vector集合将需要经常重新分配内存数组的大小,其时间开销往往是使用LinkedList时间开销的几十倍,效果差。
- 如果有多条线程需同时访问List集合中的元素,可以考虑使用Vector这个同步实现。
import java.util.Queue;//先进先出的辅助队列
import java.util.LinkedList;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
2)ArrayList
ArrayList, LinkedList ,Vector是List接口实现的一个类。接口不可以被构造(不可实例化),但是可以为接口创建一个指向自己的对象引用。List是继承于Collection接口,除了Collection通用的方法以外,扩展了部分只属于List的方法。
- ArrayList是动态数组的类,可以灵活设置数组的大小。查询快,中间插入、删除元素很慢。
import java.util.ArrayList;
ArrayList<TreeNode> List = new ArrayList<TreeNode>();
addAll()
方法用于将指定collection中的所有元素添加到列表。跟下面的Collections.sort(res);保持一致。
List<String> list=new ArrayList<String>();
list.add("保护环境"); //向列表中添加数据
ArrayList<String> a;ArrayList<String> b;a.addAll(b); //将两个Array<String>合并到一个;
ArrayList<String> res = new ArrayList<String>() ;
HashSet<String> set = new HashSet<String>();
{......}
res.addAll(set);
Collections.sort(res);
- ArrayList的一些操作方法:
set(index,element);//先做index检查,然后将index位置替换为element,并返回原来index出的对象
get(index); //先做index是否越界检查,然后返回index处的对象
remove(index); //去掉index位置的对象,在方法中还有后面对象前移的过程,效率低
(无法确定数组大小时,才用这个,因为数组扩容会大大影响此类的效率)
每当执行Add
、AddRange
、Insert
、InsertRange
等添加元素的方法,都会检查内部数组的容量是否不够了,如果是,它就会以当前容量的两倍来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组。这个临界点的扩容操作,是比较影响效率的。
- ArrayList的初始化:
执行new时,在堆内存开辟一块空间(常量池位于方法区,方法区位于堆内存)。
构造函数的底层是Object[] 数组,该数组可以存放任何对象。
3)PriorityQueue
- 保存队列元素的顺序不是按加入队列的顺序,而按队列元素的大小进行重新排序。调用
peek()
方法或者poll()
来取出队列元素时,不是取出最先进入队列的元素,而是取出队列中最小的元素; - 事实上,PriorityQueue类已经违反了队列,先入先出的基本原则。PriorityQueue不允许插入null元素;
- PriorityQueue类需要对队列元素进行排序,队列元素有两种排序方式:自然排序、定制排序。
import java.util.Queue;//先进先出的辅助队列
import java.util.LinkedList;
Queue<TreeNode> queue = new LinkedList<TreeNode>();