JDK源码学习系列之ArrayList数组

1.类继承结构

在这里插入图片描述

2.重要成员变量

/**
 * 数组默认大小.
 */
private static final int DEFAULT_CAPACITY = 10;

 /**
 * 空数组. new ArrayList(0)时使用它
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

 /**
 * 存储具体数据的数组对象
 */
transient Object[] elementData; 

 /**
 * 数组中元素个数 
 */
private int size;

/**
 * 数组被修改次数,对数组添加或者移除元素时 加1
 */
protected transient int modCount = 0;

3.重要方法

3.1 get(int index)

public E get(int index) {
   // 是否越界  
    rangeCheck(index);
   // 直接通过数组下标获取 
    return elementData(index);
}

3.2 add(E e)

public boolean add(E e) {
   // 确保容量够用
    ensureCapacityInternal(size + 1);  
    // 直接在数组后面添加入当前元素e
    elementData[size++] = e;
    return true;
}
 
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 当前不够位置插入,需要扩容,进入grow方法
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}  


private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    //  oldCapacity >> 1 表示原容量转二进制后右移1位
    // 10 二进制是1010 右移一位即 0101  得到 newCapacity  = 10 + 5,扩容后是原容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
     // 如果扩容后比需要的容量小,则取minCapacity
        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);
}

3.3 add(int index, E e)

public void add(int index, E element) {
   // 检查是否越界
    rangeCheckForAdd(index);
   // 扩容是否需要
    ensureCapacityInternal(size + 1);  // Increments modCount!! 
    // 从数组index位开始往后移动一位,一共移动 size - index个元素
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // index空出来后写入 要插入的元素
    elementData[index] = element;
    size++;
}

3.4 remove(int index)

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    // 返回当前元素
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
       // 说明要移除的不是最后一位
       //需要将index之后元素都往前移动一位,共移动size - index - 1个元素
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
        // 最后一位设置为null                 
    elementData[--size] = null; 
    return oldValue;
}

3.5 remove(Object o)

public boolean remove(Object o) {
    if (o == null) {
       // 删除null元素的情况
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
       // 非null元素 
        for (int index = 0; index < size; index++)
           // 遍历找到相等元素,然后传入index去移除
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 这里跟remove(index)一样
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 最后一位设置为null
}

3.6 addAll(Collection<? extends E> c)

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    // 是否扩容
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 从原数组末尾开始放入即将要加入的数组c,操作次数为c数组长度,即numNew
    System.arraycopy(a, 0, elementData, size, numNew);
    // 总数 = 原数组长度 + 数组c的长度
    size += numNew;
    return numNew != 0;
}

3.7 addAll(int index, Collection<? extends E> c)

public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew); 
     
    int numMoved = size - index;
    if (numMoved > 0)
        // 非插入末尾的情况
        //  原数组index以及之后的元素往后移动到目的位置:index + numNew,一个操作numMoved 个元素
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

   // 上面腾出来位置后,腾出位置大小为a数组的元素个数,将新插入的数组a放到原数组中 
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

4. 最大长度

ArrayList最大长度

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

即有符号整数的最大长度2^31 - 1 ,这里减去8是因为一些jvm的数组结构会包含头部信息,这样做目的是减少内存溢出的概率。 其实最大长度是可以到Integer.MAX_VALUE的。

5. 为什么用transient修饰

我们知道transient 关键字修饰的变量 不会 被序列化为字节流,ArrayList内部重写了writeObject()和readObject()方法,所以本身也是能序列化的,但是它并非暴力的把整个数组序列化了,因为一般真正存储的元素个数都是小于数组大小的,这样做可以减少内存空间占用。

猜你喜欢

转载自blog.csdn.net/huangdi1309/article/details/123424799