ArrayList源码学习笔记

1.本文要点:

   (1)ArrayLisrt简介:

        ArrayList底层是数组实现,查询快增删慢;那么为什么查询慢,怎么实现的呢?下面我听过分析源码来解释。

      本文介绍方法有get,set,remve,add;更多详细方法源码可参考https://blog.csdn.net/ljcITworld/article/details/52041836

   (2)System.arraycopy介绍:

       因为下面的源码有用到,考虑到有小伙伴可能记得不是很清楚这里先介绍下。

      复制指定源数组src到目标数组dest。复制从src的srcPos索引开始,复制的个数是length,复制到dest的索引从destPos开始。

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

     

2.底层源码分析:

  (1)构造函数

   通过构造方法初始化一个数组,长度默认为10,当然也可以通过构造方法自己定义初始化长度。

public ArrayList(int initialCapacity) {
	super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
	this.elementData = new Object[initialCapacity];
    }
 
  
    public ArrayList() {
	this(10);
    }
 
    public ArrayList(Collection<? extends E> c) {
	elementData = c.toArray(); //将集合转换成数组
	size = elementData.length;
	// c.toArray might (incorrectly) not return Object[] (see 6260652)
	if (elementData.getClass() != Object[].class)
	    elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

(2)添加元素:

    ArrayList中添加元素有两个方法,add(Object o)和add(index i, Object o),需要注意的是添加元素的时候可能会出现扩容的情况.

add(Object o)

add加元素的时候首先会调用扩容函数(ensureCapacityInternal不一定会扩容)进行判断是否需要扩容,通过以当前集合中元素个数(size)+1与当前集合最小容量作比较,倘若前者比较大那么就会进行扩容,扩容为原来的1.5倍。然后调用Arrays.copyOf(array,length)将原数组内容复制到新数组中,然后将新加入的值放入size位置。完成添加操作。

add(index i, Object o)

这里需要注意的是当index大于当前元素个数的时候,会抛出异常,这里的add操作是插入操作了,所以要用system.arrayCopy()方法进行数组复制;

set(index i, Object o)

设置index位置的元素值了element,返回该位置的之前的值,相当于是替换了,

public boolean add(E e) {
     // 扩容
    ensureCapacityInternal(size + 1);  
    // 将e赋值给elementData的size+1的位置。
    elementData[size++] = e;
    return true;
}
public void add(int index, E element) {
    // 判断index是否越界  
    rangeCheckForAdd(index);
     // 扩容
    ensureCapacityInternal(size + 1);  
     // 将elementData从index位置开始,复制到elementData的index+1开始的连续空间
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
     // 在elementData的index位置赋值element
    elementData[index] = element;
     // ArrayList的大小加一  
    size++;
}
public E set(int index, E element) {
     // 检查是否越界  
    rangeCheck(index);
     // 调用elementData(index)获取到当前位置的值
    E oldValue = elementData(index);
     // 将element赋值到ArrayList的elementData数组的第index位置
    elementData[index] = element;
    return oldValue;
}

(3) 扩容

添加元素的时候首先会调用ensureCapacityInternal方法

//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         // 获取默认的容量和传入参数的较大值
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}


//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 如果最小需要空间比elementData的内存空间要大,则需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}


// MAX_VALUE为231-1,MAX_ARRAY_SIZE 就是获取Java中int的最大限制,以防止越界  
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 
//扩容算法
private void grow(int minCapacity) {
    // 获取到ArrayList中elementData数组的内存空间长度
    int oldCapacity = elementData.length;
    // 扩容至原来的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组, 
    // 不够就将数组长度设置为需要的长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 判断有没超过最大限制
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
    // 并将elementData的数据复制到新的内存空间
    elementData = Arrays.copyOf(elementData, newCapacity);
}

(4)获取

get(int index)

这里就是通过数组下标返回数组,所以查就很快了
public E get(int index) {
     // 检查是否越界
    rangeCheck(index);
     // 返回ArrayList的elementData数组index位置的元素
    return elementData(index);
}



(5)移除

remove(index)

删除指定位置元素,将后面元素左移

remove(Object o)

遍历数组找到对应的object的下标,删除。

//根据下标移除元素
public E remove(int index) {
     // 判断是否越界  
    rangeCheck(index);
    modCount++;
     // 读取旧值  
    E oldValue = elementData(index);
     // 获取index位置开始到最后一个位置的个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
         // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
     // 使size-1 ,设置elementData的size位置为空,让GC来清理内存空间
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}


//根据对象移除元素
public boolean remove(Object o) {
    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++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

猜你喜欢

转载自blog.csdn.net/wind_cp/article/details/82561570