zoukankan      html  css  js  c++  java
  • Java容器解析系列(6) Queue Deque AbstractQueue 详解

    首先我们来看一下Queue接口:

    /**    
     * @since 1.5
     */
    public interface Queue<E> extends Collection<E> {
        
        // 添加指定元素,在添加失败时(队列满),抛出IllegalStateException
        boolean add(E e);
    
        // 添加指定元素,在添加失败时(队列满),返回false
        boolean offer(E e);
        
        // 删除并返回头部,当队列为空时,抛出NoSuchElementException
        E remove();
    
        // 删除并返回头部,当队列为空时,返回null
        E poll();
    
        // 获取但不删除头部,当队列为空时,抛出NoSuchElementException
        E element();
    
        // 获取但不删除头部,队列为空时,返回null
        E peek();
    
    }
    

    javadoc中对于Queue接口的表述如下:

    Queue 用来存放 优先处理元素 的集合,这种场景一般用于缓冲、并发访问。
    除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作。
    添加、删除、查询这些个操作都提供了两种形式,其中一种在操作失败时直接抛出异常,而另一种则返回一个特殊值----false或null;后一种是特地提供给容量受限的实现类的;
    (具体哪些方法抛出异常,哪些方法返回特殊值见下图)

    Queue 一般都是 FIFO 的,但是也有例外,比如优先队列 priority queue(元素顺序是根据自然排序或者自定义 comparator 排序);
    再比如 LIFO 的队列(跟栈一样,后进先出)。
    不论进入、出去的先后顺序是怎样的,使用 remove(),poll() 方法操作的都是 头部 的元素;
    而插入的位置则不一定是在队尾了,不同的 queue 会有不同的插入逻辑。
    一般情况下 Queue 的实现类不允许 null 元素,因为null被poll()/peek()方法作为一个特殊返回值,以此来表示队列为空,尽管一些实现类允许null,比如LinkedList;

    从上面的描述我们可以看出,Java中的Queue与我们在数据结构中理解的队列不一样:数据结构中的队列只允许FIFO,而Java中的Queue接口表示其可以是一个队列,但是其不必须是一个队列;

    接下来我们来看Deque接口:

    /**
     * @since  1.6
     */
    public interface Deque<E> extends Queue<E> {
        // 添加指定元素到队头,如果失败,将抛出IllegalStateException;
        // 如果是容量受限的队列,建议使用offerFirst();
        void addFirst(E e);
    
        // 添加指定元素到队尾,如果失败,将抛出IllegalStateException;
        // 如果是容量受限的队列,建议使用offerLast();
        // 该方法和add()相同;
        void addLast(E e);
    
        // 添加指定元素到队头,如果失败,返回false;
        boolean offerFirst(E e);
    
        // 添加指定元素到队尾,如果失败,返回false;
        // 该方法与offer()相同;
        boolean offerLast(E e);
    
        // 删除并返回头部,当队列为空时,抛出NoSuchElementException;
        // 该方法与remove()相同;
        E removeFirst();
    
        // 删除并返回尾部,当队列为空时,抛出NoSuchElementException;
        E removeLast();
    
        // 删除并返回头部,当队列为空时,返回null;
        // 该方法与poll()相同;
        E pollFirst();
    
        // 删除并返回尾部,当队列为空时,返回null;
        E pollLast();
    
        // 获取但不删除头部,当队列为空时,抛出NoSuchElementException;
        // 该方法与element()相同;
        E getFirst();
    
        // 获取但不删除队尾,当队列为空时,抛出NoSuchElementException;
        E getLast();
    
        // 获取但不删除头部,队列为空时,返回null;
        // 该方法与peek()相同;
        E peekFirst();
    
        // 获取但不删除队尾,队列为空时,返回null;
        E peekLast();
    
        boolean removeFirstOccurrence(Object o);
        boolean removeLastOccurrence(Object o);
    
        // *** Queue methods ***
        // 队列接口方法
        boolean add(E e);
        boolean offer(E e);
        E remove();
        E poll();
        E element();
        E peek();
    
        // *** Stack methods ***
        // 栈相关方法
    
        // 将指定元素入栈;
        // 该方法与addFirst()相同;
        void push(E e);
    
        // 弹栈;
        // 该方法与removeFirst()相同;
        E pop();
    
        // *** Collection methods ***
        // Collection接口方法
        boolean remove(Object o);
        boolean contains(Object o);
        public int size();
        Iterator<E> iterator();
        Iterator<E> descendingIterator();
    
    }
    

    javadoc中对于Deque接口的表述如下:

    Deque /dek/ :双端队列 Double ended queue
    大多数双端队列大小不确定,但是 Deque 支持容量受限的双端队列。
    Deque 接口定义了一些从头部和尾部访问元素的方法。比如分别在头部、尾部进行插入、删除、获取元素。
    每个操作都有两种方法,一种在异常情况下直接抛出异常奔溃,另一种则不会抛异常,而是返回特殊的值--false或null;后一种是特地提供给容量受限的实现类的;

    Deque接口继承自Queue,除了添加了双端队列的方法外,还定义了栈操作的方法,java.util.Stack推荐使用Deque进行栈操作;

    关于Queue和Deque的一些联系和区别,可以总结如下:

    1. 和Queue一样,增删改查,每个操作都有两种方法,一种在异常情况下直接抛出异常奔溃,另一种则不会抛异常,而是返回特殊的值---false或null:

    2. Deque中有些新添加的方法与Queue中的某些方法预期是相等的(同一种实现,不同的方法名):

    3. 栈相关方法也是通过Deque新添加的双端队列相关方法实现(同一种实现,不同的方法名):

    接下来我们来看AbstractQueue:

    /**
     * @since 1.5
     */
    public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> {
    
        protected AbstractQueue() {
        }
    
        public boolean add(E e) {
            if (offer(e))
                return true;
            else
                throw new IllegalStateException("Queue full");
        }
    
        public E remove() {
            E x = poll();
            if (x != null)
                return x;
            else
                throw new NoSuchElementException();
        }
    
        public E element() {
            E x = peek();
            if (x != null)
                return x;
            else
                throw new NoSuchElementException();
        }
    
        public void clear() {
            while (poll() != null)
                ;
        }
    
        public boolean addAll(Collection<? extends E> c) {
            // 不允许添加null
            if (c == null)
                throw new NullPointerException();
            // 不允许添加自身
            if (c == this)
                throw new IllegalArgumentException();
            boolean modified = false;
            for (E e : c)
                if (add(e))
                    modified = true;
            return modified;
        }
    
    }
    

    从源码可以看出:

    1. 该类提供Queue的骨架实现;
    2. 方法 add() ,remove(),和element()分别基于offer(),poll()和peek()实现;只是在失败时抛出异常而已,而其子类就要具体实现这些方法了;
    3. 继承该类的实现类不应该允许插入null;
    Let's go change the world,or changed by the world
  • 相关阅读:
    挺喜欢的一幅摄影作品,不知道作者 不知道出处...
    使用触发器来监控表的使用情况
    SQL Server 针对表的只读权限分配
    tnslsnr.exe进程占用大量内存的解决.
    记录一次MYSQL的备份(浅尝辄止型)
    记录temp被撑爆的一次SQL tuning
    Bug 5880921 V$SYSMETRIC_HISTORY 的时间错乱
    sqlite3学习记录
    指针 数组指针 指针数组 函数指针等说明。
    c/c++ 运算符 优先级 结合性 记录
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/9779151.html
Copyright © 2011-2022 走看看