队列(二)(Deque)

看书上讲解OkHttp源码时候,使用到了Deque,看朋友也都在用,这就有必要好好学习一心
转自:
http://blog.csdn.net/buaaroid/article/details/51315860
http://blog.csdn.net/Buaaroid/article/details/51315970
说明:
一 队列: 队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
这与我们生活中的队列是一致的,前面消费,后边插入
二 双端队列: 双端队列,顾名思义,两端都能操作(操作一下吗?),两端都可以进行插入和删除的操作,即:即可在表的前端,进行插入和删除操作,又可以在表后端进行插入和删除操作
三 栗子与应用 在OkHttp3.4.1的源码中,看到了如下的应用

public final class Dispatcher { private int maxRequests = 64; private int maxRequestsPerHost = 5; /** Executes calls. Created lazily. */ private ExecutorService executorService; /** Ready async calls in the order they'll be run. */ private final Deque readyAsyncCalls = new ArrayDeque<>(); /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. */ private final Deque runningSyncCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) { this.executorService = executorService; }

可见,OkHttp,用ArrayDeque 作为了请求队列,下面,我们来介绍ArrayDeque
1 继承关系
public class ArrayDeque extends AbstractCollection implements Deque, Cloneable, Serializable

实现了Deque、Cloneable和Serializable接口。实现Cloneable和Serializable接口的主要目的是为了实现克隆和序列化,
继承了AbstractCollection 抽象类
2 成员变量: ArrayDeque 类中,定义了4个成员变量,代码如下:
/** * The array in which the elements of the deque are stored. * The capacity of the deque is the length of this array, which is * always a power of two. The array is never allowed to become * full, except transiently within an addX method where it is * resized (see doubleCapacity) immediately upon becoming full, * thus avoiding head and tail wrapping around to equal each * other.We also guarantee that all array cells not holding * deque elements are always null. */ transient Object[] elements; // non-private to simplify nested class access/** * The index of the element at the head of the deque (which is the * element that would be removed by remove() or pop()); or an * arbitrary number equal to tail if the deque is empty. */ transient int head; /** * The index at which the next element would be added to the tail * of the deque (via addLast(E), add(E), or push(E)). */ transient int tail; /** * The minimum capacity that we'll use for a newly created deque. * Must be a power of 2. */ private static final int MIN_INITIAL_CAPACITY = 8;

  • Object[] elements;
存储deque元素的数组。 deque的容量是这个数组的长度,它总是2的幂。 该数组永远不会变为满,除了在addX方法中暂时调整大小(请参阅doubleCapacity),当它变为满时, 从而避免头部和尾部缠绕在彼此相等。 我们也保证所有不带deque元素的数组单元都是空的。简言之: Object[] elements 存储元素的 数组

  • transient int head;
在队列head 处的元素的index(索引),即,接下来调用remove() 或 pop()方法来移除的元素的索引, 如果,对列为空,这个值 = tail的值

  • transient int tail;
接下来,调用addlast() 或者 add()方法,加入队尾的元素的index(索引)

  • private static final int MIN_INITIAL_CAPACITY = 8;
我们将用于新创建的deque的最小容量。 必须是2的力量。

总结:
其中elements是元素集合数组,head是指向队首的下标,tail是指向队尾的下一个位置的下标。MIN_INITIAL_CAPACITY定义了在创建队列是,如果有指定长度,而且指定长度小于MIN_INITIAL_CAPACITY,则使用MIN_INITIAL_CAPACITY作为数组的最小长度。
3 构造函数: 构造函数有3,
  • 1 无参构造:
public ArrayDeque() { elements = new Object[16]; }

默认元素,数组大小 16
  • 2 指定大小的构造:
public ArrayDeque(int numElements) { allocateElements(numElements); }

调用了allocateElements(numElements)方法,
private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. // Tests "<=" because arrays aren't kept full. if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>>1); initialCapacity |= (initialCapacity >>>2); initialCapacity |= (initialCapacity >>>4); initialCapacity |= (initialCapacity >>>8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; if (initialCapacity < 0)// Too many elements, must back off initialCapacity >>>= 1; // Good luck allocating 2^30 elements } elements = new Object[initialCapacity]; }

制定了:元素数组的大小,如果传入的大小,小于MIN_INITIAL_CAPACITY,则使用MIN_INITIAL_CAPACITY作为数组的最小长度。
  • 2 指定元素构造:
public ArrayDeque(Collection c) { allocateElements(c.size()); addAll(c); }

传入一个元素集合,这个构造内部完成了:构造Object[] 数组,和填充元素的操作
4 常用方法: 这里要从Queue接口开始写起:
转自 : http://blog.csdn.net/Buaaroid/article/details/51315970
5 . Queue
在java5中新增加了Java.util.Queue接口,用以支持队列的常见操作。该接口扩展了java.util.Collection接口。
public interface Queue extends Collection { //... }

所以除了基本的Collection操作,队列还支持:插入,提取和检查操作
  • 类结构
队列(二)(Deque)
文章图片
img 队列(二)(Deque)
文章图片
img
:抛异常: :返回特殊值:
插入 add(e) offer(e)
移除 remove() pool()
检查 element() peek()
队列通常(但并非一定)以 FIFO(first in first out 先进先出)的方式排序各个元素。不过优先级队列和 LIFO(last in first out 后进先出) 队列(或堆栈)例外,优先级队列根据提供的比较器或元素的自然顺序对元素进行排序,LIFO队列按 LIFO的方式对元素进行排序。
在 FIFO 队列中,所有的新元素都插入队列的末尾,移除元素从队列头部移除。
Queue使用时要尽量避免Collectionadd()remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果要使用前端而不移出该元素,使用element()或者peek()方法。
返回值 方法名 说明
增加
boolean add(E e) 是Collection接口里的方法,将指定元素插入此队列的tail(如果立即可执行,且不会违反容量限制),在成功时返回true,如果当前没有可用空间,则抛出IllegalStateException
boolean offer(E e) 是Queue接口里的方法,将指定元素插入此队列的tail(如果立即可执行,且不会违反容量限制),返回true,否则返回false, 当使用有容量限制的队列时,此方法通常要优于add(E),add(e)方法可能无法插入,抛出异常,
检查
E element() 是Queue接口里的方法,返回head(队头),处的元素,但不移除此元素,如果队列empty,则抛出异常
E peek() 是Queue接口里的方法,返回head(队头),处的元素,但不移除此元素,如果队列empty,则返回null
移除
E remove() 是Queue接口里的方法,(注意,Collection里的remove(E e )方法,是有参的),获取并移除此队列的head,如果队列empty,则抛出异常NoSuchElementException
E poll() 是Queue接口里的方法,(注意,Collection里的remove(E e )方法,是有参的),获取并移除此队列的head,如果队列empty,则返回null
说明:
offer 方法可插入一个元素,否则返回 false。这与 Collection.add 方法不同,该方法只能通过抛出未经检查的异常使添加元素失败。
remove() 和 poll() 方法可移除和返回队列的头。到底从队列中移除哪个元素是队列排序策略的功能,而该策略在各种实现中是不同的。remove() 和 poll() 方法仅在队列为空时其行为有所不同:remove() 方法抛出一个异常,而 poll() 方法则返回 null。
element() 和 peek() 返回,但不移除,队列的头。
Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。
值得注意的是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
private void testDeque() {Deque tmpDeque = new LinkedList<>(); tmpDeque.offer("Hello"); tmpDeque.offer("World"); tmpDeque.offer("你好!"); Log.d(TAG, "tmpDeque.size():" + tmpDeque.size()); String str = null; while ((str = tmpDeque.poll()) != null) { Log.d(TAG, "str : " + str); } Log.d(TAG, "tmpDeque.size():" + tmpDeque.size()); }

D/DequeActivity: tmpDeque.size():3 D/DequeActivity: str : Hello D/DequeActivity: str : World D/DequeActivity: str : 你好! D/DequeActivity: tmpDeque.size():0

6 . Deque
public interface Deque extends Queue { //.... }

Deque 接口继承了Queue接口,所以支持Queue 的所有操作,间接也支持Collection的所以操作
类节构:
队列(二)(Deque)
文章图片
img Deque是一个线性 collection,支持在两端插入和移除元素。
名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。
大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
队列(二)(Deque)
文章图片
img 此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。因为此接口继承了队列接口Queue,所以其每种方法也存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。
  • 将Deque 用作单纯的队列(FIFO,尾进头出,先进先出)
【队列(二)(Deque)】在将双端队列用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。从 Queue 接口继承的方法完全等效于 Deque 方法,如下表所示:
Queue的方法 等效Deque的方法 描述
add(E e) addLast(E e) 添加至tail,失败,则抛异常
offer() offerLast() 添加至tail,失败,则返回false
remove() removeFirst() 移除head,失败,则抛异常
poll() pollFirst() 移除head,失败,返回null
element() getFirst() 检查head,不移除,失败,则抛异常
peek() peekFirst() 检查head,,不移除,失败,则返回null
队列(二)(Deque)
文章图片
img
  • 将Deque 用作栈(LIFO,头进头出,后进先出,比如activity的栈)
在将双端队列用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留 Stack 类。在将双端队列用作堆栈时,元素被推入双端队列的开头并从双端队列开头弹出。堆栈方法完全等效于 Deque 方法,如下表所示:
堆栈方法 等效Deque的方法 描述
push(e) addFirst(e) 添加至头部,失败,则抛异常
offerFirst(e) 添加至头部,失败,则返回false
pop() removeFirst() 移除头部,失败,则抛异
pollFirst() 移除头部,失败,则返回false
peek() getFirst() 检查头部,失败,则抛异常
peekFirst() 检查头部,失败,则返回null
队列(二)(Deque)
文章图片
img

    推荐阅读