关于ArrayList源码的一些自我理解以及解析(一):ArrayList的两种不同“空数据”

在首先进入ArrayList源码的时候,看到的先是它的继承关系,ArrayList一定是其他类的子类,而它的父类就是AbstractList<E>。

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

对于后续两个类Cloneable和java.io.Serializable很好理解,应该是为了实现ArrayList的克隆,和一些自定义的对象的序列化,也就是非int,double,char这种基础类型。

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

对于ArrayList而言。它默认的数组长度大小是10,并且有两个不一样的空数组。第一个Empty_ElementData,从取名就可以知道,这是一个空的对象数据;而第二个DefaultCapacity_Empty_ElementData,是默认空的对象数据。明明两个的值都是一样的,为什么要特别的区分开来呢?虽然注释里面解释了,当我还是想知道啥时候这两个会开始使用,于是我找到了构造方法。

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList的两个构造方法,一个传递参数,一个不需要传递参数。当传递的参数为0,也就是ArrayList(0)的时候,elementData将设置成为Empty_ElementData。而当不传递参数的时候,elementData将设置成为DefaultCapacity_Empty_ElementData。可是我还是搞不懂为什么要区分开来?后续找到了调用的地方。

    /**
     * Increases the capacity of this <tt>ArrayList</tt> instance, if
     * necessary, to ensure that it can hold at least the number of elements
     * specified by the minimum capacity argument.
     *
     * @param   minCapacity   the desired minimum capacity
     */
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;
        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

这段代码在注释中解释的很清楚,确保容量。在扩容之前,ArrayList会进行判断,如果说这个ArrayList在创建的时候,elementData是名字最长的那个DefaultCapacity_Empty_ElementData,那么认为这个数组最小的容量就是默认容量为10,否则就是0.这里使用的是一个三目运算符来判断。

这里我就差不多理解了,如果在使用ArrayList时,我们传递了一个为0的int参数,那么,ArrayList就会认为,要建立一个长度为0的对象数组。如果我们不传递参数,ArrayList就创建一个空对象数组,但是,当你后续对这个数组进行操作,比如,想让它成为数组长度为2的ArrayList。这时候,人家就不会这么麻烦再去确保最小的容量了,因为你当时不传递参数进去,多长也不知道,ArrayList自动帮你视为长度为10的数组对象。嗯,我觉得是这样的。后续会进入ensureExplicitCapacity的方法中,接着看下去。

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

果然,和我预想的差不多。但是,等等,有一个陌生的变量混了进来?modCount是什么时候定义的?于是我找遍了整个ArrayList也没有找到定义这个modCount的代码行数,于是我觉得它应该是在父类里面。于是进入了父类的源码寻找,果然立马就找到了。这里贴上代码:

    /**
     * The number of times this list has been <i>structurally modified</i>.
     * Structural modifications are those that change the size of the
     * list, or otherwise perturb it in such a fashion that iterations in
     * progress may yield incorrect results.
     *
     * <p>This field is used by the iterator and list iterator implementation
     * returned by the {@code iterator} and {@code listIterator} methods.
     * If the value of this field changes unexpectedly, the iterator (or list
     * iterator) will throw a {@code ConcurrentModificationException} in
     * response to the {@code next}, {@code remove}, {@code previous},
     * {@code set} or {@code add} operations.  This provides
     * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
     * the face of concurrent modification during iteration.
     *
     * <p><b>Use of this field by subclasses is optional.</b> If a subclass
     * wishes to provide fail-fast iterators (and list iterators), then it
     * merely has to increment this field in its {@code add(int, E)} and
     * {@code remove(int)} methods (and any other methods that it overrides
     * that result in structural modifications to the list).  A single call to
     * {@code add(int, E)} or {@code remove(int)} must add no more than
     * one to this field, or the iterators (and list iterators) will throw
     * bogus {@code ConcurrentModificationExceptions}.  If an implementation
     * does not wish to provide fail-fast iterators, this field may be
     * ignored.
     */
    protected transient int modCount = 0;

仅仅对这一个变量的注释,就比一个方法的注释还要多,看来这个modCount肯定很重要。在撇脚的英语下,我勉强读懂了注释,这个数的作用,就是为了记录整个list改动的次数或者说是整个这些东西被改动的次数,而这个数在iterators中很重要。如果稍有不慎,在使用next,set,add,remove,previous这些方法时就会抛出异常。现在还不是很需要了解这个,只需要理解成为记录数组变动次数的int值。咱们继续看下去:

     /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

这个grow方法,在整个ArrayList源码中只在ensureExplicitCapacity使用,也就是说,ArrayList需要扩大容量上限的时候才会调用ensureCapacityInternal去扩大。比如说,一个ArrayList有自己的默认容量大小,当你实际的容量超过了ArrayList给你预留的容量大小后,它才会去增大它的预留容量。这也就是newCapacity为什么是在oldCapacity的基础上只增大了(oldCapacity>>1)的原因,为了尽量少的去占用内存(应该是这样。)

总结:

当我们新建一个ArrayList的时候,如果没有指定大小,且没有使用ensureCapacity去预设它的大小(容量),那么它会为你预留一个长度为10的数组。当你下次添加对象进去时,就不用再次去专门扩大最小存储容量。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值