java源码分析之ArrayList
ArrayList的继承关系:
- ArrayList类继承了AbstractList类并实现了List接口,实现Cloneable和Serializable接口。
- ArrayList具有克隆和序列化的功能。
- ArrayList底层是数组。
属性:
//默认初始容量为10
private static final int DEFAULT_CAPACITY = 10;
//调用无参数构造函数时默认创建一个空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据的数组
transient Object[] elementData;
//ArrayList的实际元素数量
private int size;
构造方法:
/**
*参数为指定容量的构造函数
*当initialCapacity=0时,使用EMPTY_ELEMENTDATA创建一个空数组
*当initialCapacity<0时,抛出异常
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}
/**
*无参构造函数
*创建一个初始容量为10的空list。
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
*参数为Collection的构造函数
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
//elementData.length赋值给size
//判断Collection不为空,Collection转为数组赋值给elementData
if ((size = elementData.length) != 0) {
//判断elementData 是否转为object数组
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//判断Collection不空,用EMPTY_ELEMENTDATA初始化list
this.elementData = EMPTY_ELEMENTDATA;
}
}
下面再来看一下ArrayList的一些方法
add和扩容机制:
//在ArrayList末尾插入元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//在指定位置插入元素
//基本同add(E e)一致
public void add(int index, E element) {
rangeCheckForAdd(index);//位置有效性检查
ensureCapacityInternal(size + 1); // Increments modCount!!
//在指定位置插入元素,arraycopy实际是一个native方法(后面有补充)
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//如果elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA,返回默认容量10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//判断ArrayList是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//每操作一次list modCount+1
//超出了数组容纳的最大长度,需要进行动态扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//动态扩容
private void grow(int minCapacity) {
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);
//重新创建一个容量为newCapacity的数组,并将原数组的值复制到新数组
//tip:创建数组时,最好预估数组容量,以防数据过大,复制数据影响效率
elementData = Arrays.copyOf(elementData, newCapacity);
}
get方法:
public E get(int index) {
rangeCheck(index);//检查有效性
//ArrayList底层实现为数组,所以直接利用数组索引来取值就好
return elementData(index);
}
set方法:
public E set(int index, E element) {
rangeCheck(index);//检查有效性
//获取旧值
E oldValue = elementData(index);
//替换旧值
elementData[index] = element;
//返回旧值
return oldValue;
}
remove方法:
//按照索引删除元素
public E remove(int index) {
rangeCheck(index);//判断索引是否超出ArrayList的size
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;//复制多少个元素
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//此方法下面补充
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
//按照元素删除
//按照元素找出ArrayList中第一次出现元素的位置,通过fastRemove(index)删除
//如果存在这个元素返回true,如果不存在返回false
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;
}
//同remove没有很大的区别
//去掉了索引是否超出范围的校验,因为这个索引都是remove(Object o)
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
clear方法:
public void clear() {
modCount++;
//遍历将ArrayList每个值置为null
for (int i = 0; i < size; i++)
elementData[i] = null;
//将ArrayListsize置为0
size = 0;
}
总结:
总结了ArrayList的继承关系,属性,构造方法,常用方法(add,get.set,clear,remove),通过分析源码,加深了自己对ArrayList的理解.
补充:
关于数组复制本不打算再写,但是总感觉缺点什么,本着追求完美的精神,还是总结一下了,涉及到下面两个函数
Arrays.copyOf(elementData, size);
//Arrays.copyOf(elementData, size)底层就是arraycopy
//src:源数组
//srcPos:源数组要复制的起始位置
//dest:目标数组
//destPos:目标数组要复制的起始位置
//length:复制的长度
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
public static void test6(){
int[] arr1 = {1,2,3,4,5};
int[] arr2 = {6,7,8,9,0};
System.arraycopy(arr1, 2, arr2, 2, 3);
System.out.println(Arrays.toString(arr2));
}
上图test6()方法中输出是[6, 7, 3, 4, 5]
System.arraycopy(arr1, 2, arr2, 2, 3)的含义就是arr1从下标2开始复制3个元素,加在并覆盖数组arr2下标2后面的元素