1 特点
- 有序的,可以存储重复值和null值。
- 底层是数组实现的,线程不安全。通过下标查询、速度快,非末尾增删的时候需要重新移动数据,所以增删性能低。
- 调用构造函数new ArrayList()时,最开始是一个空数组,在第一次add的时候会创建一个初始容量为10的数组。也可以自定义初始化容量new ArrayList(int initialCapacity)。
- 以自身的1.5倍容量扩容,不可以设置容量增量。
2 源码分析
2.1 接口的定义和属性
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认的初始容量,在不指定minCapacity变量的时候,它将使用DEFAULT_CAPACITY 作为数组的大小。但是需要注意的是它是在第一次添加元素的时候在使用的,而不是像指定了minCapacity在构造方法中那样在创建ArrayList 的过程中就创建了。
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 指定参数初始容量,但是初始容量是0的时候
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 初始容量是0的数组。无参构造时候,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA 其实主要是为了区分elementData=它是无参构造的赋值,当有参构造方法但是参数是0的时候的赋值
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 实际存储元素的数组
*/
transient Object[] elementData;
/**
* 实际存储的元素个数,而不是数组的大小
*/
private int size;
}
2.2 构造方法
//ArrayList构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
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);
}
}
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
- 当调用new ArrayList<>()时,将一个空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了elementData,这个时候集合的长度size为默认长度0,在第一次添加的时候就会把容量变为10;
- 当调用new ArrayList<>(100)时,根据传入的长度,new一个Object[100]赋值给elementData,当然如果玩儿的话,传了一个0,那么将一个空数组 EMPTY_ELEMENTDATA 赋值给了elementData;
- 当调用new ArrayList<>(new HashSet())时,可以传递任何实现了Collection接口的类,将传递的集合调用toArray()方法转为数组内赋值给elementData。
2.3 add(E element)
//添加一个元素到列表的末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1);//在添加元素之前要保证数组大小可以容纳该元素
elementData[size++] = e;
return true;
}
//如果是首次添加元素的话,szie=0 则 minCapacity=1
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算容量大小
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果无参数构造的话,elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在第一次添加元素的时候将直接扩容到DEFAULT_CAPACITY,直接返回DEFAULT_CAPACITY默认容量10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//否则的话则是有参构造或者不是第一次添加元素,那么这里返回的就是size+1,也就是说扩容到所需的szie+1 即可
return minCapacity;
}
//确保足够的容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 当实际需要的最小容量大于数组的容量,不满足要求的容量则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//右移动一位相当于除以2,新的容量大小就相当于原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果newCapacity 还是不足的话,直接使用minCapacity作为最小容量
//当无参构造方法创建时第一次调用grow方法minCapacity=10,而newCapacity=0,所以无参构造方法默认的初始容量为10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//创建一个容量为newCapacity的数组,并把数组elementData元素复制到新的数组里
elementData = Arrays.copyOf(elementData, newCapacity);
}
//容量最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//当超出最大值进行处理
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2.4 add(int index, E element)
//插入指定元素到指定位置,如果当前待插入的位置有元素,则需要右移当前元素和其后的元素
public void add(int index, E element) {
// 检查位置的合法性
rangeCheckForAdd(index);
//跟add里面的一样,保证数组容量足够
ensureCapacityInternal(size + 1);
// 移动当前位置和其后置的元素
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 检查位置的合法性
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.5 set(int index, E element)
public E set(int index, E element) {
//数组下标是否越界
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
//数组下标是否越界
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.6 get(int index)
public E get(int index) {
//数组下标是否越界
rangeCheck(index);
return elementData(index);
}
2.7 remove(int index) 和 remove(Object o)
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);
//通过设置null值让GC起作用
elementData[--size] = null;
return oldValue;
}
//删除ArrayList中的值对象,其实和通过下标删除很相似
//只是多了一个步骤,遍历底层数组elementData,通过equals()方法或 == (特殊情况下)来找到要删除的元素,获取其下标
//最后调用remove(int index)一样的代码即可。
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;
}
//移除
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;
}
ArrayList 还提供了另外一个方法,就是removeIf(),它是一个函数式接口
List<String> cityList = new ArrayList<>(2);
cityList.removeIf((String name )->name.equalsIgnoreCase("Bangalore"));
2.8 iterator()
//移除失败的场景
@Test
public void itratorTest(){
List<String> cityList = new ArrayList<>(4);
cityList.add("重庆");
cityList.add("北京");
cityList.add("上海");
cityList.add("天津");
Iterator<String> itr = cityList.iterator();
while(itr.hasNext()){
String city = itr.next();
if(city.equals("北京")){
cityList.remove(city);
}
System.out.println(city);
}
}
输出结果
重庆
北京
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
//移除成功的场景
@Test
public void itratorTest2(){
List<String> cityList = new ArrayList<>(4);
cityList.add("重庆");
cityList.add("北京");
cityList.add("上海");
cityList.add("天津");
Iterator<String> itr = cityList.iterator();
while(itr.hasNext()){
String city = itr.next();
if(city.equals("北京")){
itr.remove();
}
System.out.println(city);
}
}
2.9 其他方法
int size() : 获取集合长度,通过定义在ArrayList中的私有变量size得到
boolean isEmpty():是否为空,通过定义在ArrayList中的私有变量size得到
boolean contains(Object o):是否包含某个元素,通过遍历底层数组elementData,通过equals或==进行判断
int lastIndexOf(Object o):返回最后一个元素为o的下标
Object[] toArray():转换成一个新得数组返回
void clear():集合清空,通过遍历底层数组elementData,设置为null