美文网首页Java容器
【java容器的刻意练习】【十四】ArrayDeque的源码分析

【java容器的刻意练习】【十四】ArrayDeque的源码分析

作者: 程序猿修仙传 | 来源:发表于2020-02-20 23:00 被阅读0次

    上一篇看了ArrayDeque的添加元素,这篇我们来看删除元素是怎样实现的。

        /**
         * @throws NoSuchElementException {@inheritDoc}
         */
        public E removeFirst() {
            E e = pollFirst();
            if (e == null)
                throw new NoSuchElementException();
            return e;
        }
    

    原来removeFirst调用了pollFirst,我们来看看pollFirst

        public E pollFirst() {
            final Object[] es;
            final int h;
            E e = elementAt(es = elements, h = head);
            if (e != null) {
                es[h] = null;
                head = inc(h, es.length);
            }
            return e;
        }
    

    调用了elementAtelements获取了head位置的元素:

        static final <E> E elementAt(Object[] es, int i) {
            return (E) es[i];
        }
    

    因为是数组,所以直接通过下标访问即可,时间复杂度O(1)。

    然后将head元素置空,通过head = inc(h, es.length);将head位置加一。如果超过数组长度,根据循环数组的特点,变为数组第0个下标。

    pollLast实现也是差不多的,就不多啰嗦了。

    peekFirstpeekLast就更简单了,直接通过elementAt通过数组下标快速访问元素即可:

        public E peekFirst() {
            return elementAt(elements, head);
        }
    
        public E peekLast() {
            final Object[] es;
            return elementAt(es = elements, dec(tail, es.length));
        }
    

    由于是数组,所以极度不推荐删除元素的,所以removeFirstOccurrenceremoveLastOccurrence这2个极度浪费性能操作能不用尽量不用。

        /**
         * Removes the first occurrence of the specified element in this
         * deque (when traversing the deque from head to tail).
         * If the deque does not contain the element, it is unchanged.
         * More formally, removes the first element {@code e} such that
         * {@code o.equals(e)} (if such an element exists).
         * Returns {@code true} if this deque contained the specified element
         * (or equivalently, if this deque changed as a result of the call).
         *
         * @param o element to be removed from this deque, if present
         * @return {@code true} if the deque contained the specified element
         */
        public boolean removeFirstOccurrence(Object o) {
            if (o != null) {
                final Object[] es = elements;
                for (int i = head, end = tail, to = (i <= end) ? end : es.length;
                     ; i = 0, to = end) {
                    for (; i < to; i++)
                        if (o.equals(es[i])) {
                            delete(i);
                            return true;
                        }
                    if (to == end) break;
                }
            }
            return false;
        }
    

    这个removeFirstOccurrence就是删除第一个找到的元素。如果双端队列不包含某个元素,那么就不会变。如果该元素被删除,就会使得数组的元素向前或向后运动。 delete函数作者已经优化了算法,使得每次挪动最少的元素。

        /**
         * Removes the element at the specified position in the elements array.
         * This can result in forward or backwards motion of array elements.
         * We optimize for least element motion.
         *
         * <p>This method is called delete rather than remove to emphasize
         * that its semantics differ from those of {@link List#remove(int)}.
         *
         * @return true if elements near tail moved backwards
         */
        boolean delete(int i) {
            final Object[] es = elements;
            final int capacity = es.length;
            final int h, t;
            // number of elements before to-be-deleted elt
            final int front = sub(i, h = head, capacity);
            // number of elements after to-be-deleted elt
            final int back = sub(t = tail, i, capacity) - 1;
            if (front < back) {
                // move front elements forwards
                if (h <= i) {
                    System.arraycopy(es, h, es, h + 1, front);
                } else { // Wrap around
                    System.arraycopy(es, 0, es, 1, i);
                    es[0] = es[capacity - 1];
                    System.arraycopy(es, h, es, h + 1, front - (i + 1));
                }
                es[h] = null;
                head = inc(h, capacity);
                return false;
            } else {
                // move back elements backwards
                tail = dec(t, capacity);
                if (i <= tail) {
                    System.arraycopy(es, i + 1, es, i, back);
                } else { // Wrap around
                    System.arraycopy(es, i + 1, es, i, capacity - (i + 1));
                    es[capacity - 1] = es[0];
                    System.arraycopy(es, 1, es, 0, t - 1);
                }
                es[tail] = null;
                return true;
            }
        }
    

    每删除一个元素,都要挪动其他元素,非常划不来。那如果用了ArrayDeque,但是迫不得已要删除元素,怎么办?我建议在保存的元素Object里面添加一个叫isDelete的boolean属性。使用前先判断isDelete即可。这就像居委大妈跟普通大妈区别,就是袖子上多一个红袖标,大大写着“居委”两字。

    相关文章

      网友评论

        本文标题:【java容器的刻意练习】【十四】ArrayDeque的源码分析

        本文链接:https://www.haomeiwen.com/subject/gfbfqhtx.html