zoukankan      html  css  js  c++  java
  • 基于JDK1.8的ArrayList剖析

    前言

    本文是基于JDK1.8的ArrayList进行分析的。本文大概从以下几个方面来分析ArrayList这个数据结构

    • 构造方法
    • add方法
    • 扩容
    • remove方法

    (一)构造方法

     1 /**
     2  * Constructs an empty list with the specified initial capacity.
     3  *
     4  * @param  initialCapacity  the initial capacity of the list
     5  * @throws IllegalArgumentException if the specified initial capacity
     6  *         is negative
     7  */
     8 public ArrayList(int initialCapacity) {
     9     if (initialCapacity > 0) {
    10         this.elementData = new Object[initialCapacity];
    11     } else if (initialCapacity == 0) {
    12         this.elementData = EMPTY_ELEMENTDATA;
    13     } else {
    14         throw new IllegalArgumentException("Illegal Capacity: "+
    15                                            initialCapacity);
    16     }
    17 }
    18 
    19 /**
    20  * Constructs an empty list with an initial capacity of ten.
    21  */
    22 public ArrayList() {
    23     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    24 }

    总所周知,ArrayList底层是数组

    我们先看第二个构造方法,即无参构造方法(第22行),将默认空数组赋值给Object数组。这一段执行完,就在堆中分配了一段空的数组。

    我们再看第一个构造方法,有参构造方法。

    1 /**
    2 * 加入我们有这么一段代码
    3 */
    4 List<String> list = new ArrayList<>(10);

    其构造方法有三个分支,initialCapacity大于0,等于0,小于0。我们直接看大于0,其他两个看代码就懂了。

    他会直接构造一个以initialCapacity为大小的Object数组。

    (二)add方法

     1 /**
     2  * Appends the specified element to the end of this list.
     3  *
     4  * @param e element to be appended to this list
     5  * @return <tt>true</tt> (as specified by {@link Collection#add})
     6  */
     7 public boolean add(E e) {
     8     ensureCapacityInternal(size + 1);  // Increments modCount!!
     9     elementData[size++] = e;
    10     return true;
    11 }

    size这个字段为数组的长度。然后进入ensureCapacityInternal这个方法。

    1 private void ensureCapacityInternal(int minCapacity) {
    2     if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    3         minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    4     }
    5 
    6     ensureExplicitCapacity(minCapacity);
    7 }

    可以看出来,第二行是判断是否位第一次add,也就是初始化。如果数组位空,那么DEFAULT_CAPACITY就是为10。

    初始化完成,也就是开辟一个大小为10的Object数组。

    1 private void ensureExplicitCapacity(int minCapacity) {
    2     modCount++;
    3 
    4     // overflow-conscious code
    5     if (minCapacity - elementData.length > 0)
    6         grow(minCapacity);
    7 }

    minCapacity是list里面元素的个数,比如第一次放,也就是10,elementData.length也就是数组的长度,也就是0。

    那么减法结果就为 10,不成立就不扩容,如果成立就扩容。

    1 elementData[size++] = e;

    然后返回add方法,执行这一句,至此,add方法就执行完毕了.接下来我们看下扩容的代码。

    (三)扩容

     1 /**
     2  * 我们假设第一次扩容,list数组容量为10,再次add那么传过来的minCapacity为11
     3  * oldCapacity为10
     4  * newCapacity为 10 + (10 / 2) = 15 也就是扩容1.5倍
     5  * 有两个if的极值判断,看代码很简单就懂了
     6  * 然后执行Arrays.copy方法
     7  */
     8 private void grow(int minCapacity) {
     9     // overflow-conscious code
    10     int oldCapacity = elementData.length;
    11     int newCapacity = oldCapacity + (oldCapacity >> 1);
    12     if (newCapacity - minCapacity < 0)
    13         newCapacity = minCapacity;
    14     if (newCapacity - MAX_ARRAY_SIZE > 0)
    15         newCapacity = hugeCapacity(minCapacity);
    16     // minCapacity is usually close to size, so this is a win:
    17     elementData = Arrays.copyOf(elementData, newCapacity);
    18 }

    我看了下Arryas的copy方法,也不难,大家可以对照着源码自己去看一下。

    至此ArrayList的add方法就介绍完了,grow方法也搞定了。

    (四)remove方法

     1 /**
     2  * remove方法总共有两个,一个是按照index删除,一个是按照元素删除,大同小异
     3  * 下面说按照index删除,看注释
     4  */
     5 public E remove(int index) {
     6     //首先检查是否越界
     7     rangeCheck(index);
     8 
     9     modCount++;
    10     E oldValue = elementData(index);
    11 
    12     int numMoved = size - index - 1;
    13     //把后面的全部移到前面一位,System.arraycopy是一个native方法
    14     if (numMoved > 0)
    15         System.arraycopy(elementData, index+1, elementData, index,
    16                          numMoved);
    17     //然后最后一位置为空值,gc会自动回收
    18     elementData[--size] = null; // clear to let GC do its work
    19 
    20     return oldValue;
    21     }

    remove方法很简单,直接看我写的注释即可。

    (五)总结

    写了两个多小时,我也醉了。主要我在调试eclipse的debug,我的那个有点问题,eclipseEE版本就没有问题。

    其实我有一点疑惑,希望大佬能够给我解答以下。

    初始化构造方法那里,并没有给size赋值,也就是为null值,不能直接输出或者使用(会报错,已实验)

    但调用list.size()方法的时候,返回的确是0,没有报错,我debug也没看出来什么时候赋值了。

    大佬看到这篇文章,希望可以解答下。谢谢。

    -------------------------------华丽分割线----------------------------

    好累啊

  • 相关阅读:
    sudo: 在加载插件“sudoers_policy”时在 /etc/sudo.conf 第 0 行出错 sudo: /usr/lib/sudo/sudoers.so 必须只对其所有者可写 sudo:
    ubuntu多版本Python和pip切换
    Ubuntu16.04下安装python3.6.4详细步骤
    oenstack firewalld ufw
    linux相关安全设置
    TransPose: Towards Explainable Human Pose Estimation by Transformer
    TransTrack: Multiple-Object Tracking with Transformer
    Rethinking Semantic Segmentation from a Sequence-to-Sequence Perspective with Transformers
    Adversarial Sparse Transformer for Time Series Forecasting
    Learning to Detect Objects with a 1 Megapixel Event Camera
  • 原文地址:https://www.cnblogs.com/wenbochang/p/8466205.html
Copyright © 2011-2022 走看看