ArrayList

ArrayList底层就是一个可以grow扩容的数组Object[] elementData

1 成员属性
// 默认的数组大小
private static final int DEFAULT_CAPACITY = 10;

// 当没有设置初始化大小时候,所有实例的elementData共享指向
private static final Object[] EMPTY_ELEMENTDATA = {};

// 设置了初始化大小,但是设置为0时,所有实例的elementData共享指向
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

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

// 实际存储的元素大小,而不是elementData的长度
private int size;

2 主要方法

2.1增加元素

public boolean add(E e) {
        // 判断是否需要扩容,如果需要则扩容数组
        ensureCapacityInternal(size + 1);
        // 就是底层数组对应元素指向e,size++既控制了数组index,又记录了当前元素总数
        elementData[size++] = e;
        return true;
}

// minCapacity = size + 1; 如果elementData.length <= size + 1,即数组实际长度不足 size + 1时候,就会触发扩容
// 很简单道理:数组长度至少要保证比size多1,才能保证能插入到数组中

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        //  elementData.length 没有大于 size + 1,那么就插入不到新元素,需要扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

private void grow(int minCapacity) {
        int oldCapacity = elementData.length;

        // oldCapacity 扩容为以前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 将元素从老组数拷贝到扩容后的新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

2.2 删除元素

src源数组(被拷贝的数组)
srcPos源数组拷贝的起始位置
dest目标数组(需要被更新的数组)
destPos目标数组被更新的起始位置
length源数组的长度
static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

2.2.1 根据index删除元素
public E remove(int index) {  
        // 根据index获取老的元素,删除返回老元素
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            // 把删除元素index之后的元素,全部向前平移一位,被删除index自然被覆盖了
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 清空原来数组的最后一位,利于垃圾回收
        elementData[--size] = null; // clear to let GC do its work
    }

2.2.2 根据对象删除元素
 public boolean remove(Object o) {
        // 无论o是否为null,都是循环遍历删除,根据定位到的index,去数组删除
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                // 非空对象采用equals()方法进行比较,所以注意重写equal,同样通过index删除
                if (o.equals(elementData[index])) {
                    // 底层同样采用System.arraycopy(src[], srcPos, des[], desPos, length)进行组数覆盖
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

2.3 modCount

在我迭代器迭代操作时候,ArrayList不允许被我迭代器之外的修改(add、remove等);检查核心:迭代器内部Itr的计数器expectedModCount和外部类ArrayList的modCount是否相等;


class ArrayLIst {
   // 调用ArrayLIst的实例方法add,remove都会修改modCount的值 
   protected transient int modCount = 0;

    // 因为ArrayLIst实现Iterable接口,要求返回iterator迭代器
    public Iterator<E> iterator() {
        return new Itr();
    }

    // ArrayLIst内部自己定义一个迭代器类Iterator
    private class Itr implements Iterator<E> {
       
        // 内部类可以直接引用外部类的实例属性,且由于是简单类型,所以创建内部类时候有一份独立拷贝
        // 这个独立拷贝在迭代时候可以和外部比较是否修改过

        int expectedModCount = modCount;

        Itr(){}
        
        public E next() {
            。。。
            // 迭代过程检查是否修改
            checkForComodification();
            。。。
        }

        public void remove() {
            。。。
            checkForComodification();
            // 调用外部实例的方法,会修改外部的modCount
            ArrayList.this.remove(lastRet);
            // 如果是迭代器自身修改的ArrayList,会同步外部最新modCount到expectedModCount ,所以迭代器自己检查时候不              // 会引发检查错误
            expectedModCount = modCount;
            。。。
        }
        
        final void checkForComodification() {
            // 发现内部expectedModCoun和外部modCount 不同时,会触发异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

对于迭代器引发ConcurrentModificationException的总结:
为了维护保护迭代器在迭代过程中能够完整遍历所有数据(保证底层数组没有修改过);无论是同一个线程中对实例list使用list.remove();还是其他线程使用list.remove()或者iterator.remove()都会导致modCount != expectedModCount触发异常

2.4 在内部类中调用外部类的实例属性ArrayList.this.elementData和方法ArrayList.this.remove()
// 外部类
public class ArrayList{
   
   // 外部类属性
   transient Object[] elementData;
   // 外部类方法
   public E remove(int index) {......}
   
   // 内部类
   private class Itr implements Iterator<E> {

       public E next() {
            // 内部类中调用外部类的实例属性
            Object[] elementData = ArrayList.this.elementData;
        }

        public void remove() { 
                // 内部类中调用外部类的实例方法
                ArrayList.this.remove(lastRet);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值