arrayqueue源码_thinkphp源码分析

arrayqueue源码_thinkphp源码分析愉快地聊一聊ArrayDeque的特点吧~(以下都是基于jdk1.8)一棵树ArrayDeque的继承树如下图:基本特点(1)双端队列,可从两端添加、删除元素。作为队列使用时,性能优于LinkedList。作为栈使用时,性能优于Stack。(2)底层使用可变数组Object[]elements,数组容量按需增长(3)不能存储null(4)支持双向迭代器遍历(5)线程不安全…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

愉快地聊一聊ArrayDeque的特点吧~(以下都是基于jdk1.8)

一棵树

ArrayDeque的继承树如下图:
在这里插入图片描述

基本特点

(1)双端队列,可从两端添加、删除元素。作为队列使用时,性能优于LinkedList。作为栈使用时,性能优于Stack。

(2)底层使用可变数组Object[] elements, 数组容量按需增长

(3)不能存储null

(4)支持双向迭代器遍历

(5)线程不安全

(6)fail-fast机制。

(7)最小数组容量MIN_INITIAL_CAPACITY = 8。Must be a power of 2

(8)默认数组初始容量是16

(9)调用指定初始容量的构造函数,并不会按照指定值分配容量。

(10)先添加,再判断是否需要扩容

源码之旅

这里只取部分源码进行分析:指定初始容量的构造函数、扩容机制,以及主要方法。

好了,先把类中定义的变量熟悉一下:

	 /** * 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;

(1)指定初始容量的构造函数:

找到该构造函数,从此入手:

    /** * Constructs an empty array deque with an initial capacity * sufficient to hold the specified number of elements. * * @param numElements lower bound on initial capacity of the deque */
    public ArrayDeque(int numElements) { 
   
        allocateElements(numElements);
    }

再看allocateElements方法:

    /** * Allocates empty array to hold the given number of elements. * * @param numElements the number of elements to hold */
    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];
    }

拿给定元素数量与数组最小容量8做比较,因为此集合不允许数组变满(添加元素的方法中,数组容量一满就立刻扩容),所以当给定元素数量>=数组最小容量8时,会进行一系列的无符号右移运算、或运算,以便找到能够容纳给定元素的最佳的2的幂次方。

这个最佳的2的幂次方就是调用该构造函数后底层为我们分配的数组容量。

(2)扩容机制:

找到扩容的核心方法:

    /** * Doubles the capacity of this deque. Call only when full, i.e., * when head and tail have wrapped around to become equal. */
    private void doubleCapacity() { 
   
    	//断言 判断head与tail指针是否相等
        assert head == tail;
        int p = head;
        int n = elements.length;
        int r = n - p; // number of elements to the right of p
        //左移1位,相当于*2操作,只是<<效率要高于*运算。
        int newCapacity = n << 1;
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        //扩容,实际上是定义了一个指定容量的数组,将elements数组中的元素复制到新数组a中。
        Object[] a = new Object[newCapacity];
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        //重新设置head和tail的指针
        elements = a;
        head = 0;
        tail = n;
    }

(3)主要方法:

添加元素:

    // The main insertion and extraction methods are addFirst,
    // addLast, pollFirst, pollLast. The other methods are defined in
    // terms of these.

    /** * Inserts the specified element at the front of this deque. * * @param e the element to add * @throws NullPointerException if the specified element is null */
    public void addFirst(E e) { 
   
        if (e == null)
            throw new NullPointerException();
        //此运算可以快速定位到要插入的位置,实际上是从数组最右侧开始插入的,head是递减的
        elements[head = (head - 1) & (elements.length - 1)] = e;
        //head与tail重叠时,开始扩容
        if (head == tail)
            doubleCapacity();
    }
    
     /** * Inserts the specified element at the end of this deque. * * <p>This method is equivalent to {@link #add}. * * @param e the element to add * @throws NullPointerException if the specified element is null */
    public void addLast(E e) { 
   
        if (e == null)
            throw new NullPointerException();
        //tail初始值是0,指向待插入元素的位置,tail是递增的
        elements[tail] = e;
        //先插入元素,再判断是否需要扩容。
        //tail + 1 & (elements.length - 1 )用于定位下一个待插入元素的位置。
        //如果tail与head重叠,数组容量已满,
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
     
     /** * Inserts the specified element at the front of this deque. * * @param e the element to add * @return {@code true} (as specified by {@link Deque#offerFirst}) * @throws NullPointerException if the specified element is null */
    public boolean offerFirst(E e) { 
   
        addFirst(e);
        return true;
    }

    /** * Inserts the specified element at the end of this deque. * * @param e the element to add * @return {@code true} (as specified by {@link Deque#offerLast}) * @throws NullPointerException if the specified element is null */
    public boolean offerLast(E e) { 
   
        addLast(e);
        return true;
    }

删除首尾元素:

    public E removeFirst() { 
   
        E x = pollFirst();
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }
    
    public E removeLast() { 
   
        E x = pollLast();
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    public E pollFirst() { 
   
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];
        // Element is null if deque empty
        if (result == null)
            return null;
        elements[h] = null;     // Must null out slot
        head = (h + 1) & (elements.length - 1);
        return result;
    }

    public E pollLast() { 
   
        int t = (tail - 1) & (elements.length - 1);
        @SuppressWarnings("unchecked")
        E result = (E) elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        tail = t;
        return result;
    }

删除指定元素:

	/** * 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)
            return false;
        int mask = elements.length - 1;
        int i = head;
        Object x;
        while ( (x = elements[i]) != null) { 
   
            if (o.equals(x)) { 
   
                delete(i);
                return true;
            }
            i = (i + 1) & mask;
        }
        return false;
    }

    /** * Removes the last 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 last 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 removeLastOccurrence(Object o) { 
   
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = (tail - 1) & mask;
        Object x;
        while ( (x = elements[i]) != null) { 
   
            if (o.equals(x)) { 
   
                delete(i);
                return true;
            }
            i = (i - 1) & mask;
        }
        return false;
    }

    private void checkInvariants() { 
   
        assert elements[tail] == null;
        assert head == tail ? elements[head] == null :
            (elements[head] != null &&
             elements[(tail - 1) & (elements.length - 1)] != null);
        assert elements[(head - 1) & (elements.length - 1)] == null;
    }

    /** * Removes the element at the specified position in the elements array, * adjusting head and tail as necessary. This can result in motion of * elements backwards or forwards in the array. * * <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 moved backwards */
    private boolean delete(int i) { 
   
        checkInvariants();
        final Object[] elements = this.elements;
        final int mask = elements.length - 1;
        final int h = head;
        final int t = tail;
        final int front = (i - h) & mask;
        final int back  = (t - i) & mask;

        // Invariant: head <= i < tail mod circularity
        if (front >= ((t - h) & mask))
            throw new ConcurrentModificationException();

        // Optimize for least element motion
        if (front < back) { 
   
            if (h <= i) { 
   
                System.arraycopy(elements, h, elements, h + 1, front);
            } else { 
    // Wrap around
                System.arraycopy(elements, 0, elements, 1, i);
                elements[0] = elements[mask];
                System.arraycopy(elements, h, elements, h + 1, mask - h);
            }
            elements[h] = null;
            head = (h + 1) & mask;
            return false;
        } else { 
   
            if (i < t) { 
    // Copy the null tail as well
                System.arraycopy(elements, i + 1, elements, i, back);
                tail = t - 1;
            } else { 
    // Wrap around
                System.arraycopy(elements, i + 1, elements, i, mask - i);
                elements[mask] = elements[0];
                System.arraycopy(elements, 1, elements, 0, t);
                tail = (t - 1) & mask;
            }
            return true;
        }
    }

暂时先写到这里了~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/191478.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 值得收藏!15个 Pythonic 的代码示例

    值得收藏!15个 Pythonic 的代码示例Python 由于语言的简洁性 让我们以人类思考的方式来写代码 新手更容易上手 老鸟更爱不释手 要写出 Pythonic 优雅的 地道的 整洁的 代码 还要平时多观察那些大牛代码 Github 上有很多非常优秀的源代码值得阅读 比如 requests flask tornado 这里小明收集了一些常见的 Pythonic 写法 帮助你养成写优秀代码的习惯 01 变量交换 Badtmp aa bb tmpPythonica b b a02 列表推导 Badmy list

    2025年6月7日
    0
  • R语言做文本挖掘 Part4文本分类

    R语言做文本挖掘 Part4文本分类

    2022年1月10日
    37
  • Verilog 流水线设计[通俗易懂]

    Verilog 流水线设计[通俗易懂]一、什么是流水线流水线设计就是将组合逻辑系统地分割,并在各个部分(分级)之间插入寄存器,并暂存中间数据的方法。目的是将一个大操作分解成若干的小操作,每一步小操作的时间较小,所以能提高频率,各小操作能并行执行,所以能提高数据吞吐率(提高处理速度)。二、什么时候用流水线设计使用流水线一般是时序比较紧张,对电路工作频率较高的时候。典型情况如下:1)功能模块之间的流水线,用乒乓buffer来交互数据。代价是增加了memory的数量,但是和获得的巨大性能提升相比,可以忽略不计。2)I/O瓶

    2022年8月14日
    1
  • NFS服务器搭建(配置web服务器)

    NFS服务简介什么是NFS?NFS挂载原理:RPC与NFS通讯原理:NFS客户端和NFS服务器通讯过程:Linux下NFS服务器部署NFS服务所需软件及主要配置文件:服务端安装NFS服务步骤:NFS客户端挂载配置:在Window上挂载NFS

    2022年4月13日
    83
  • js数组删除元素_js清空数组的方法

    js数组删除元素_js清空数组的方法js的数组删除,我建议大家使用splice函数,不要使用slice函数,因为slice是返回一个新数组,并不是从原来的数组中删除。比如:leta=[111,222,333,444];a.splice(2,1);上面的代码运行后,a数组的值变成:[111,222,444]假如用slice实现:leta=[111,222,333,444];letb=a.slice(2,1);这时a的值不会改变,而b的值变成了[111,222,444]所以splice是比slice用起来简单的

    2022年10月1日
    0
  • route-map的原理及简单应用「建议收藏」

    route-map的原理及简单应用「建议收藏」route-map(路由策略)

    2022年7月1日
    25

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号