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