ArrayList底层是用数组实现的存储。与它类似的是LinkedList,和LinkedList相比,它的查找和访问元素的速度较快,但新增,删除的速度较慢。
ArrayList是线程不安全的,一般常用于查询操作,如果涉及频繁的增删,可以使用LinkedList,如果你需要线程安全可以使用Vector
一、ArrayList属性
//序列号
private static final long serialVersionUID = 8683452581122892189L;
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//一个空数组,当用户指定ArrayList容量为0时,返回该数组
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 一个空数组实例
* -- 当用户没有指定 ArrayList 的容量时(即调用无参构造函数),返回的是该数组==>刚创建一个
* ArrayList时,其内数据量为 0。
* -- 当用户第一次添加元素时,该数组将会扩容,变成默认容量为 10(DEFAULT_CAPACITY) 的一
* 个数组===>通过ensureCapacityInternal() 实现
* 它与 EMPTY_ELEMENTDATA 的区别就是:该数组是默认返回的,而后者是在用户指定容量为 0 时返回
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//当前数据对象存放地方,当前对象不参与序列化
//这个关键字最主要的作用就是当序列化时,被transient修饰的内容将不会被序列化
transient Object[] elementData; // non-private to simplify nested class access
//ArrayList实际存储的数据数量
private int size;
//继承于AbstractList
//集合数组修改次数的标识
protected transient int modCount = 0;
二、 ArrayList构造函数
/**
创建指定容量的ArrayList
*/
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);
}
}
/**
* 无参构造函数:
* - 创建一个空的 ArrayList,此时其内数组缓冲区 elementData = {}, 长度为 0
* - 当元素第一次被加入时(第一次add元素时),扩容至默认容量 10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 创建一个包含collection的ArrayList
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
三、常用方法
1、add(E e)将指定的元素追加到此列表的末尾。
/**
*增加指定的元素到ArrayList的最后位置
* @param e 要添加的元素
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//取最大值
minCapacity= Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
/**
* 判断数组是否主要扩容
*/
private void ensureExplicitCapacity(int minCapacity) {
// 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
modCount++;
/**
* 到此步元素还未添加到数组里
* 判断数组真实元素个数加1后的长度与当前数组长度大小关系,
* 如果小于0,数组还有容量可以add值,返回,如果大于0,说明无容量,则扩容
* 使用无参构造,第一次add,此时elementData数组为空,minCapacity - elementData.length>0,
* 此时grow()方法会分配初始容量,有指定容量构造,elementData不为空,第一次add无需扩容
*/
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 数组缓冲区最大存储容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 数组扩容,以确保 ArrayList 至少能存储 minCapacity 个元素
* 扩容计算:newCapacity = oldCapacity + (oldCapacity >> 1); 扩充当前容量的1.5倍
*/
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 运算符 >> 是带符号右移. 扩大1.5倍
// 如 oldCapacity = 10,则 newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) // 若 newCapacity 依旧小于 minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 若 newCapacity 大于最大存储容量,则进行大容量分配
newCapacity = hugeCapacity(minCapacity);
//把原数组元素复制到扩容后的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* 私有方法:大容量分配,最大分配 Integer.MAX_VALUE
* @param minCapacity
*/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
使用无参构造,底层数组elementData为空,在第一次add的时候才会分配DEFAULT_CAPACITY = 10的初始容量。有指定容量构造,则使用指定容量。在真正添加元素之前,会先判断数组是否需要扩容。
2.add(int index, E element)在此列表中的指定位置插入指定的元素。
public void add(int index, E element) {
//判断索引是否越界
rangeCheckForAdd(index);
//确保已使用数组长度size+1能够存下数据
ensureCapacityInternal(size + 1); // Increments modCount!!
//elementData:要复制的数组,index从哪里开始复制
//elementData:目标数组,index复制到的数组第几个开始,最后一个是复制长度
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
System.arraycopy(elementData, index, elementData, index + 1, size - index);是怎么操作的如图:
在index1上插入d,由index1开始复制,放到index+1的位置上,把index1位置空出来,然后进行插入操作。当数据量很大时,速度会有影响。
3.get(int index)返回此列表中指定位置的元素。
这个方法很简单,就是通过索引查找元素
public E get(int index) {
//判断索引是否越界
rangeCheck(index);
return elementData(index);
}
4.remove(int index)删除该列表中指定位置的元素。
public E remove(int index) {
//判断索引是否越界
rangeCheck(index);
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;
}
System.arraycopy(elementData, index+1, elementData, index,numMoved);如图:
删除index1上元素,就是把index+1开始复制,复制到index上,相当于覆盖原来index1上的元素。
本篇文章就说那么多了,其他代码大家可以自行查看,应该都能看明白。
ArrayList特点:查询效率高,增删效率低,线程不安全。使用频率很高。
参考:https://www.cnblogs.com/gxl1995/p/7534171344218b3784f1beb90d621337.html