从源码看Android常用的数据结构 ( SDK23版本 ) ( 三 , Queue篇)

此系列文章放在了我的专栏里, 欢迎查看
https://blog.csdn.net/column/details/24187.html

相关衔接
从源码看Android常用的数据结构 ( SDK23版本 ) ( 一 , 总述 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 二, List篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 三 , Queue篇)
从源码看Android常用的数据结构 ( SDK23版本 ) ( 四, Set篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 五, Map篇 )
从源码看Android常用的数据结构 ( SDK23版本 ) ( 六, ConcurrentHashMap )
从源码看Android常用的数据结构 ( 七, SDK28下的HashMap )

Github里有一份Android使用到的小技术, 欢迎查看:
https://github.com/YouCii/LearnApp


总览

先翻译下官方注释, 了解其特性和设计目的.

A collection designed for holding elements prior to processing.
Besides basic {
   @link java.util.Collection Collection} operations,
queues provide additional insertion, extraction, and inspection
operations.  Each of these methods exists in two forms: one throws
an exception if the operation fails, the other returns a special
value (either <tt>null</tt> or <tt>false</tt>, depending on the	
operation).  The latter form of the insert operation is designed
specifically for use with capacity-restricted <tt>Queue</tt>
implementations; in most implementations, insert operations cannot
fail.

Queue是一种用于在处理前持有elements的集合。除了基本的Collection操作之外,
Queue提供了额外的插入、提取和检查操作。每个方法都有两种形式:一种是操作失败时
抛出异常, 另一种是返回model或者根据操作类型返回null以及false。
后一种插入操作形式是专门设计于Queue有容量限制的实现类, 因为在大多数实现类里,
插入操作不允许失败。

<p>Queues typically, but do not necessarily, order elements in a
FIFO (first-in-first-out) manner.  Among the exceptions are
priority queues, which order elements according to a supplied
comparator, or the elements' natural ordering, and LIFO queues (or
stacks) which order the elements LIFO (last-in-first-out).
Whatever the ordering used, the <em>head</em> of the queue is that
element which would be removed by a call to {
   @link #remove() } or
{
   @link #poll()}.  In a FIFO queue, all new elements are inserted at
the <em> tail</em> of the queue. Other kinds of queues may use
different placement rules.  Every <tt>Queue</tt> implementation
must specify its ordering properties.

队列通常但不一定都是按照FIFO(先入先出)规则排列元素。比如PriorityQueue,它根据
提供的比较器或元素的自然排序来排列元素; 再比如遵循LIFO(后进先出)规则的LIFO队列
(或Stack堆栈)。无论使用的排序规则是什么,队列的head就是会被remove()或者poll()
移除的那个元素。在FIFO队列中,所有新的元素都会被插入到队列的尾部。其他类型的队列
可能会使用不同的放置规则。每个Queue的实现类都必须指定其排序属性。

The {
   @link #offer offer} method inserts an element if possible,
otherwise returning <tt>false</tt>.  This differs from the {
   @link
java.util.Collection#add Collection.add} method, which can fail to
add an element only by throwing an unchecked exception.  The
<tt>offer</tt> method is designed for use when failure is a normal,
rather than exceptional occurrence, for example, in fixed-capacity
(or &quot;bounded&quot;) queues.

offer()会插入一个元素, 插入失败会返回false. 它和Collection接口中的add()不一样,
add()只会在抛出UncheckedException时失败.offer()适用于插入失败并不是错误情况的情
景, 比如说插入到一个固定大小或者有边界的队列中时.

The {
   @link #remove()} and {
   @link #poll()} methods remove and
return the head of the queue.
Exactly which element is removed from the queue is a
function of the queue's ordering policy, which differs from
implementation to implementation. The <tt>remove()</tt> and
<tt>poll()</tt> methods differ only in their behavior when the
queue is empty: the <tt>remove()</tt> method throws an exception,
while the <tt>poll()</tt> method returns <tt>null</tt>.
The {
   @link #element()} and {
   @link #peek()} methods return, but do
not remove, the head of the queue.

remove()poll()方法会移除并返回队列的顶部元素. 确切的说, 到底会移除哪个元素取决于不同实
现类的队列排序规则.remove()poll()只有一点不同, 就是在queue是空的情况下, remove()会抛
出异常,poll()会返回null.
element()peek()也会返回顶部元素, 但是不会移除它.

The <tt>Queue</tt> interface does not define the <i>blocking queue
methods</i>, which are common in concurrent programming.  These methods,
which wait for elements to appear or for space to become available, are
defined in the {
   @link java.util.concurrent.BlockingQueue} interface, which
extends this interface.

Queue接口没有定义那些并发编程里常用的阻塞队列方法, 等待元素生产或者空间可用的那些相关方法
被定义在了继承Queue的BlockingQueue接口里.

<tt>Queue</tt> implementations generally do not allow insertion
of <tt>null</tt> elements, although some implementations, such as
{
   @link LinkedList}, do not prohibit insertion of <tt>null</tt>.
Even in the implementations that permit it, <tt>null</tt> should
not be inserted into a <tt>Queue</tt>, as <tt>null</tt> is also
used as a special return value by the <tt>poll</tt> method to
indicate that the queue contains no elements.

Queue的实现类一般不允许插入null元素, 尽管某些实现类并没有禁止null的插入, 比如说
LinkedList. 不过就算允许插入null的这些实现类, 也不应该插入null, 因为有时候也会通过
poll()是否返回null来判断queue是否是空的.

<tt>Queue</tt> implementations generally do not define
element-based versions of methods <tt>equals</tt> and
<tt>hashCode</tt> but instead inherit the identity based versions
from class <tt>Object</tt>, because element-based equality is not
always well-defined for queues with the same elements but different
ordering properties.

Queue的实现类一般都不会定义基于元素的实体判断方法equals/hashCode, 而是直接继承Object里
的基于对象身份的equals/hashCode方法. 这是因为基于对象的相等判断对于元素相同但是排序规则不
同的Queue并不能很好的匹配.

总的来说, Queue队列额外提供了压入/弹出等操作, 这些操作执行的元素顺序由其实现类自定义.

入队 出队 检索 区别
offer() poll() peek() 执行失败时return false或null
add() remove() element() 执行失败时抛出异常

Queue 接口的主要实现中 BlockingQueue 和 Deque 是接口

  1. BlockingQueue 定义了 并发编程常用的阻塞方法, 而Queue队列使用最多的还是在并发编程情况下, 所以核心在于BlockingQueue的实现类.
  2. Deque 是双端队列, 相当于Queue+Stack, 功能冗余, 一般不怎么用, 简单看下定义的额外方法
    头部入队|尾部入队|头部出队|尾部出队|头部检索|尾部检索|区别
    -|-|-|-|-|-|
    offerFirst(E)|offerLast(E)|pollFirst()|pollLast()|peekFirst()|peekLast()|失败返回false或null
    addFirst(E)|addLast(E)|removeFirst()|removeLast()|getFirst()|getLast()|失败抛出异常

而实现类主要分为三类:
1. 非线程安全: LinkedList, PriorityQueue 等;
2. 线程安全, 非阻塞: ConcurrentLinkedQueue;
3. 线程安全, 阻塞: BlockingQueue实现类: ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue

大体区别在此

下面主要看下 PriorityQueue/BlockingQueue/ConcurrentLinkedQueue相关实现类.


PriorityQueue

A PriorityQueue holds elements on a priority heap, which orders the elements
according to their natural order or according to the comparator specified at
construction time. If the queue uses natural ordering, only elements that are
comparable are permitted to be inserted into the queue.
The least element of the specified ordering is the first retrieved with
{
   @link #poll()} and the greatest element is the last.
A PriorityQueue is not synchronized. If multiple threads will have to access
it concurrently, use the {
   @link java.util.concurrent.PriorityBlockingQueue}.

PriorityQueue使用有优先级的堆(其实还是一个数组)维护元素, 元素的排序规则根据自然顺序或者
定义在构造方法里的比较器来确定. 如果queue使用了自然排序, 那么插入此queue的元素必须实现
comparable接口.
使用poll()弹出元素时, 最小的元素会优先弹出, 最大的元素排在最后.
PriorityQueue不是同步的, 如果有多线程会同时访问的话, 请使用PriorityBlockingQueue.

从源码看, PriorityQueue 的优先级特性体现在这几个方法中, 下面在注释中进行说明

public class PriorityQueue<E> extends AbstractQueue<E> implements Serializable {
   
	/**
	* 构造方法中传入要使用的比较器, 这个比较器就是体现优先级的关键, 当然传入null的话就
	* 使用自然排序了
	*/
	public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
   
        if (initialCapacity < 1) {
   
            throw new IllegalArgumentException("initialCapacity < 1: " + initialCapacity);
        }
        elements = newElementArray(initialCapacity);
        this.comparator = comparator;
    }
	/**
	* 压入一个元素, growToSize会实现扩容, 变为传入int值的两倍大小;
	* 压入成功之后, 把新元素排序.
	*/
	public boolean offer(E o) {
   
        if (o == null) {
   
            throw new NullPointerException("o == null");
        }
        // 扩容
        growToSize(size + 1);
        elements[size] = o;
        // 排序
        siftUp(size++);
        return true;
    }
	/**
	* 先把elements[index]替换成倒数第二个元素, 原来的elements[index]就已经被remove掉了;
	* 然后, 把原来的倒数第二个元素向后移, 把倒数第一个元素置空(因为已经少了一个了);
	* 如果原倒数第二个元素执行完后移操作之后仍然在原位置, 就往前移.
	*/
	private void 
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值