版本:jdk1.8
ArrayList 数组列表
底层由数组实现
线程不安全
适合随机查找、更新(通过下标 时间复杂度为O(1))
不适合大量的新增、删除(涉及数组的移动)
底层数据结构:数组
ArrayList相关的UML
进入源码
看看有哪些属性
/**
* 默认容量 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 长度为0数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默认长度为0数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 对象数组 存放数据的结构 序列化时会忽略
*/
transient Object[] elementData;
/**
* 集合大小
*/
private int size;
/**
* 数组的最大大小
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
再看具体的源码之前,先来看看数组的Arrays.copyOf底层实现
最终是由System.arraycopy(src, srcPos, dest, destPos, length)来实现从原数组的copy数据到扩容后的新数组
src 源数组 srcPos 源数组要复制的起始下标位置 dest 目标数组 destPos 目标数组起始下标位置 length 要复制的长度
理解这里很重要,因为很多地方都会涉及到数组的拷贝
先来看看几个构造
/**
* 根据initialCapacity初始化elementData
* @param initialCapacity 初始化数组的大小
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 初始化initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 初始化个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
/**
* elementData默认长度为0数组
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 直接把elementData指向集合转成的数组
* @param c 集合
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//把elementData指向集合转成的数组
if ((size = elementData.length) != 0) {//如果用户传入的集合不为空
if (elementData.getClass() != Object[].class)//数组的类型不是对象数组就要copy一份原数组到对象数组类型
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;//空数组
}
}
ArrayList.add
可以在末尾、指定位置插入数据,里面会涉及数组的扩容,新数组的大小为原数组的1.5倍并将原数组的数据copy到新数组中再把要添加的数据添加到新的数组中,整个扩容的流程就在这下面了,后面有涉及的话,代码也就不贴出来了
/**
* 默认是末尾添加
*/
public boolean add(E e) {
//数组扩容的处理 需要扩容的话 新数组的大小为原数组的1.5 并将原数组的数据copy到新数组中 elementData指向新数组
ensureCapacityInternal(size + 1);
elementData[size++] = e;// 赋值 数组大小加一
return true;
}
/*
* 在index位置添加数据
*/
public void add(int index, E element) {
rangeCheckForAdd(index);//检测是否越界 下标不能大于等于当前集合大小且下标不能小于0
ensureCapacityInternal(size + 1); // 数组扩容
//数组自身移动 从index下标开始的(size - index)个数 每个数据的下标向后移动一位 下标index正好空出来
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;//赋值
size++;//数组大小加一
}
/**
* 数组扩容
*/
private void ensureCapacityInternal(int minCapacity) {//minCapacity 所需的最小容量大小
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 当所需的最小容量大小大于当前数组的大小 数组扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//修改次数加一
// overflow-conscious code
if (minCapacity - elementData.length > 0)// 所需的最小容量大小大于当前数组的大小
grow(minCapacity);// 数组扩容
}
/**
* 扩容
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;// 当前容量
// 相当于 newCapacity = 1.5 * oldCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将当前数组中的数据copy到长度为newCapacity的新数组中 elementData指向新的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList.get
通过index获取数组中的数据,所以ArrayList随机访问很快
/**
* 获取下标为index的数据
*/
public E get(int index) {
rangeCheck(index);// 下标范围的校验
return elementData(index);// 获取下标为index的数据
}
/**
* 获取下标为index的数据
*/
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
ArrayList.set
在指定位置覆盖原来的值
/**
* 用element覆盖掉下标为index的数据 并返回旧值
*/
public E set(int index, E element) {
rangeCheck(index);// 数组越界校验
E oldValue = elementData(index);// 获取到旧值
elementData[index] = element;// 重新赋值
return oldValue;// 返回旧值
}
ArrayList.remove
删除集合中的数据
/**
* 删除对象o
* o为null 用==比较
* 否者用equals
* 找到之后相等对象的下标 快速删除
*/
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);//快速删除 下标为index的数据
return true;
}
}
return false;
}
/*
* 快速删除 下标为index的数据
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)//数组自身移动 从(index+1)下标开始的numMoved个数 每个数据的下标向前移动一位 下标(size-1)正好空出来
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
elementData[--size] = null; // 赋null 之前指向的数据会被垃圾回收器处理
}
**需要注意的是:**在使用ArrayList.subList的时候要特别注意,获取到的List,里面会有原ArrayList的引用
/**
* 生成一个SubList对象 当前的Arraylist会被作为一个属性
* 阿里java开发手册说道
* 2. 【强制】ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常:
* java.util.RandomAccessSubList cannot be cast to java.util.ArrayList;
* 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,
* 对于SubList子列表的所有操作最终会反映到原列表上。
* 3. 【强制】在subList场景中,高度注意对原集合元素个数的修改,
* 会导致子列表的遍历、增加、删除均产生ConcurrentModificationException 异常。
*/
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
这里只是贴出了一些比较重要的方法,想要查看更完整的ArrayList源码解析点击这里