行业知识
java-01-基础篇-04 Java集合-03-Queue
一,Queue简介
队列是一种常见的数据结构,它遵循先进先出(FIFO)的原则。队列的头部元素是在队列中存储时间最长的元素(AA)。队列的尾部是在队列中停留时间最短的元素(NN)。在队列的尾部插入新元素,队列检索操作在队列的头部获取元素。如下图所示:
Queue 队列接口是在JDK1.5之后提供的;Queue队列是单向队列的接口,Queue队列接口除了继承Collection拥有基本集合业务操作之外;队列还提供了额外的插入(),删除,检查元素等队列需要的业务功能;
从Queue队列继承图和官网文档中可以得知;Queue队列的插入功能[add(E), offer(E)], 删除功能[remove(), poll()],还有检查功能[element(), peek()] 都提供两个方法;它们之间主要区别在于操作失败之后是否抛会异常;官网文档中表示插入,删除,检查这些方法都提供了两种形式。
- 一种方法是在操作失败时抛出异常;
- 另一种方法是在操作失败时返回特殊值( null / false);
add(E), remove(), element() 是在操作失败时抛出异常;
offer(E), poll(), peek() 是在操作失败时返回特殊值(null/ false)
官网文档还表明,后面这种操作失败返回特殊值的处理是专门为有容量限制的队列而提供的,因为在大多设计当中,插入操作不会失败!
二,Queue单向队列继承结构
三,Queue 接口
package java.util;/** * @since 1.5 * @author Doug Lea * @param the type of elements held in this queue */public interface Queue extends Collection { /** * 将指定元素插入到队列中,成功时返回true,如果当前没有可用空间则抛出IllegalStateException */ boolean add(E e); /** * 将指定元素插入到队列中,成功时返回true,失败返回 false */ boolean offer(E e); /** * 检索并删除队列的头部元素。此方法与poll()的唯一不同之处在于,如果此队列为空,它将抛出异常 NoSuchElementException */ E remove(); /** * 检索并删除队列的头部元素,如果此队列为空,则返回null。 */ E poll(); /** * 检索队列的头部。此方法与peek的唯一不同之处在于,如果此队列为空,它将抛出异常。NoSuchElementException */ E element(); /** * 检索队列的头部,如果此队列为空则返回null。 */ E peek();}Queue 单向队列接口定义了单向队列需要的业务方法(API);为单向队列业务提供了标准的业务接口;开发者直接调用对应的方法即可;对于开发者不需要知道具体实现是什么;这也是面向接口而非实现编程的思想;
面向接口而非实现编程的思想,通常是指在设计和编写代码时,应该依赖于抽象(这个抽象可以是接口,也可以抽象类),而不是具体实现类
因为接口为软件组件/业务定义了清晰的行为契约,而不依赖于具体实现,所以,只要满足了接口的约定,就可以自由地更换具体实现子类,而不会影响到依赖于这些接口的代码;(这样就有很好的解耦合)
同时,也提高系统对于这一块业务的扩展;这种抽象层次的提升使得开发者可以在不修改现有代码的情况下,引入新的实现子类,进行扩充;
四,AbstractQueue 抽象类
4.1 AbstractQueue 构造器
package java.util;/** * @since 1.5 * @author Doug Lea * @param the type of elements held in this queue */public abstract class AbstractQueue extends AbstractCollection implements Queue { /** 该构造器由 protected 关键字修饰 */ protected AbstractQueue() { }}AbstractQueue 接口实现了Queue 接口,还继承了AbstractCollection 意味着有Collection 集合接口的功能;
构造器使用 protected 关键字修饰;我们都知道,protected 关键字修饰的属性,方法,构造只能在同一包下才能进行访问;但是Java里的抽象类的构造器是不能进行 new ;那同包下也无法使用?那作用是什么?
提到 protected 关键字,该关键字除了在同一包下引用范围做了限制,还提供了只要是这个类的子类;即使在不同的包中也能进行访问。也就是不同包下的子类可以访问该属性,方法;对于抽象类,当然是用来继承;根据自己的业务来实现一个队列子类;这样及对外提供扩展的同时也有着封闭作用!
4.2 AbstractQueue 新增业务
/** 新增指定元素,成功返回true, 失败抛IllegalStateException异常 */ public boolean add(E e) { if (offer(e)) return true; else throw new IllegalStateException("Queue full"); }对于add(E) 操作失败抛异常的实现;内部调用的还是 offer(E) 方法;该方法操作失败并不会抛异常,而是返回true或false; 所以,AbstractQueue 队列 add()默认的实现方式就是借助offer来实现的。
4.3 AbstractQueue 删除业务
public E remove() { E x = poll(); // 获取头部的元素,并删除 if (x != null) return x; else throw new NoSuchElementException(); }该remove()删除业务,也是借助 poll() 来判断获取头部的元素是否为空,如果为空则抛异常;
4.4 AbstractQueue 查询业务
public E element() { E x = peek(); if (x != null) return x; else throw new NoSuchElementException(); }通过该方法可以得知;所有因操作失败而抛异常的业务方法;都是借助与对应的操作失败不抛异常,返回特殊值的方法(null/ boolean)进行实现的。
AbstractQueue 抽象类定义了操作失败抛异常的业务模块,而具体的子类只需要实现对应返回特殊值的方法(0ffer(), poll(), peek());
4.5 AbstractQueue 批量新增
public boolean addAll(Collection) { SortedSet