ArrayList的思考随笔

 

一、 elementData变量为什么使用transient关键字修饰?

    transient Object[] elementData; // non-private to simplify nested class access

首先引用他人对transient关键字的解释:Java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。

对于ArrayList源码中elementData使用transient关键字,按照我的理解,数组是按照1.5倍的机制扩容,所以每个elementData会留有一定的余量,如果把这个对象数组全都存进磁盘的话,会造成一定程度上的资源浪费,因此在序列化时不会按照默认算法将这个成员变量写入磁盘。这并不是说ArrayList不能把数据内容序列化了,而是使用了writeObject方法,序列化时会调用这个方法将其数据持久化,在反序列化是,调用readObject,将其读出来。附上两个方法的源码:

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            int capacity = calculateCapacity(elementData, size);
            SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

二、ArrayList的扩容机制

每一进行add的操作时,会确定一个最小的容量需求minCapacity(size+1),

如果elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA的话,就从minCapacity和DEFAULT_CAPACITY取最大值,然后根据minCapacity对当前容量长度进行1.5倍的扩容,如果扩容后仍然不满足minCapacity的需求,则直接扩大到minCapacity。

部分源码:

 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);
    }

三、关于变量modCount的作用

protected transient int modCount = 0;

初看代码并不知晓这个变量是用来做什么的,阅读abstractList源码的注释可以理解。

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

翻译过来的意思简单来说就是这个变量用来记录数组结构上被修改的次数(主要是指改变list的大小),它作用于迭代器的使用过程,当迭代器在使用的过程中,这个变量发生编发就会抛出一个ConcurrentModificationException。所以要对数组的元素进行操作只能使用迭代器提供的remove等方法。

四、面经

基于数组实现,无容量的限制。 在执行插入元素时可能要扩容,在删除元素时并不会减小数组的容量,在查找元素时要遍历 数组,对于非 null 的元素采取 equals 的方式寻找。 是非线程安全的。 注意点:

(1)ArrayList 随机元素时间复杂度 O(1),插入删除操作需大量移动元素,效率较低

(2)为了节约内存,当新建容器为空时,会共享 Object[] EMPTY_ELEMENTDATA = {}和 Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}空数组

(3)容器底层采用数组存储,每次扩容为 1.5 倍

(4)ArrayList 的实现中大量地调用了 Arrays.copyof()和 System.arraycopy()方法,其实 Arrays.copyof()内部也是调用 System.arraycopy()。System.arraycopy()为 Native 方法

(5)两个 ToArray 方法 Object[] toArray()方法。该方法有可能会抛出 java.lang.ClassCastException 异常 <T> T[] toArray(T[] a)方法。该方法可以直接将 ArrayList 转换得到的 Array 进行整体向下转 型

(6)ArrayList 可以存储 null 值

(7)ArrayList 每次修改(增加、删除)容器时,都是修改自身的 modCount;在生成迭代 器时,迭代器会保存该 modCount 值,迭代器每次获取元素时,会比较自身的 modCount 与 ArrayList 的 modCount 是否相等,来判断容器是否已经被修改,如果被修改了则抛出异 常(fast-fail 机制)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值