一、ArrayList简介
ArrayList是实现了List接口的动态数组,元素允许为null,ArrayList是有序的。
注意,ArrayList实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。所以为了保证同步,最好的办法是在创建时完成,以防止意外对列表进行不同步的访问:
List list = Collections.synchronizedList(new ArrayList(…));
二、ArrayList源码解析
ArrayList实现List接口,底层使用数组实现,因此ArrayList的实现多是对数组进行操作。
2.1底层实现数组
transient Object[] elementData;
在讨论transient之前,有必要先搞清楚Java中序列化的含义:
1. Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息, 一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输, 一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。
2.当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢, 所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。
关于transient关键字:
Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化,这一看好像很好理解,就是不被序列化,那么什么情况下,一个对象的某些字段不需要被序列化呢?如果有如下情况,可以考虑使用关键字transient修饰:
1、类中的字段值可以根据其它字段推导出来,如一个长方形类有三个属性:长度、宽度、面积(示例而已,一般不会这样设计),那么在序列化的时候,面积这个属性就没必要被序列化了;
2、其它,看具体业务需求吧,哪些字段不想被序列化;
2.2ArrayList的其他成员变量
// 序列值
private static final long serialVersionUID = 8683452581122892189L;
// 默认的初始容量,值为10
private static final int DEFAULT_CAPACITY = 10;
// 空的数组对象实例
private static final Object[] EMPTY_ELEMENTDATA = {};
// 默认容量的空数组对象实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// Arraylist包含数据的条数
private int size;
ArrayList提供了两个数组实例对象,EMPTY_ELENENTDATA 空数组实例和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA带有初始容量的对象实例,初始容量为10.
2.3构造方法
ArrayList提供了三种构造方法:
1.public ArrayList() ;默认构造方法,该方法提供一个初始容量为10的空数组实例,jdk1.6默认初始容量为10,jdk1.7及以上默认初始容量 为0;
2.public ArrayList(int initialCapacity);构造一个指定初始容量的空列表
3.public ArrayList(Collection<? extends E> c);构造一个包含指定元素集的列表,这些元素按照该元素集迭代器返回的序列进行排列。
/**
* 默认构造方法 提供一个初始容量为10的空列表(空数组) jdk1.6默认初始容量是10, jdk1.7默认初始容量是0
*
* */
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造一个指定初始容量的空列表(空数组) initialCapacity ----指定初始容量
*
* */
public ArrayList(int initialCapacity) {
// 当初始容量大于0,则构建一个指定容量的数组列表
// 当初始容量为0时,则构建一个空的数组列表
// 当初始容量小于0时,则抛出IllegalArgumentException异常
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "
+ initialCapacity);
}
}
/**
* 构造一个包含指定collection元素的列表, 这些元素按照该collection迭代器返回他们的序列 排列的
*
* */
public ArrayList(Collection<? extends E> c) {
// 将collection 转换成数组对象
elementData = c.toArray();
// 如果collection的大小不为0
if ((size = elementData.length) != 0) {
// collection对象可能不能转换成object对象,有时候可能转成String
// 参考http://www.cnblogs.com/cmdra/p/5901793.html
// 底层的toArray实现方式不同的原因
// 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;
}
}
2.4ArrayList的扩容机制
ArrayList默认情况下提供一个默认容量,内部扩容时,ArrayList会先判断当前列表是否是默认初始容量列表(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA),如果是则默认初始容量(DEFAULT_CAPACITY)与待扩容最小容量(minCapacity)比较,取二者之间较大的;然后进行二次确认,判断最小扩容容量(minCapacity)与当前列表长度(elementData.length)比较,如果待扩容容量较大,则进行内存分配;在分配容量时会先提供一个1.5倍的新容量,如果新容量比最小容量小,则最小容量为新容量;然后新容量再与默认最大容量(Integer.MAX_VALUE - 8)比较,如果该容量大于最大容量,则分配最大容量,否则就是该新容量;
ArrayList的扩容机制是一个不断的进行容量比较和再确认的过程;详细过程见源码分析:
/*
* 确认是否增加容量 minCapacity 最小容量
*
* */
public void ensureCapacity(int minCapacity) {
// 最小扩展容量,如果列表实例 不是默认容量的空列表,则最小扩展容量为0,否则最小扩展容量是10,
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
// 如果最小容量 大于扩展容量,则确认增加容量,扩展最小容量个空间,否则不进行扩展
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
/**
* 内部自动扩展
*
* */
private void ensureCapacityInternal(int minCapacity) {
// 如果列表是默认空列表,则最小容量是,最小容量和默认容量中最大值,
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 扩展minCapacity个空间,再次确认
ensureExplicitCapacity(minCapacity);
}
/**
* 再次确认是否扩容
*
* */
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 首先判断是否下溢,如果不下溢,则进行扩容
// 防止修改容量后导致,数组溢出
// overflow-conscious code
// 只能防止明显的内存溢出:(即 minCapacity -
// elementData.length最大值达到Integer.MAX_VALUE)
// 当minCapacity - elementData.length最大值小于等于Integer.MAX_VALUE
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 定义最大数组容量,整型最大值-8 通常由于一些虚拟机需要在数组的头部保留一些信息,如果分配最大值,则可能导致数组内存溢出(OutofMemory)
* 请求的数组大小超过虚拟机的限制
* */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 扩容--容量大小为最小容量值
*
* */
private void grow(int minCapacity) {
// 内存溢出意识
// overflow-conscious code
// 得到原来的容量
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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* 最大容量
*
* */
private static int hugeCapacity(int minCapacity) {
// 如果最小扩容<0,则抛出OutOfMemoryError
// 如果minCapacity超过Integer.MAX_VALUE,则minCapacity是负数
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果最小扩容大于数组最大容量,则返回整型数最大值,否则返回最大数组容量
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
2.5新增
ArrayList提供如下几个新增方法:
1.public boolean add(E e);在列表尾追加一个新元素e;
2.public boolean add(E e);在指定位置处添加一个新元素,该位置及之后的元素后移;
3.public boolean addAll(Collection<? extends E> c);追加指定的元素集到列表尾;
4.public boolean addAll(int index, Collection<? extends E> c);在指定的位置添加元素集,该位置及之后的元素后移指定元素集个数的位置;
/**
* 在列表尾追加新的元素,
*
* */
public boolean add(E e) {
// 首先判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 在指定位置添加元素,该位置及之后元素后移
* */
public void add(int index, E element) {
// 范围检查
rangeCheckForAdd(index);
// 内部容量检查
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将该位置及其后元素后移一位
System.arraycopy(elementData, index, elementData, index + 1, size
- index);
// 替换该位置元素
elementData[index] = element;
//列表的长度加一
size++;
}
/**
* 添加指定Collection元素到表尾
*
* */
public boolean addAll(Collection<? extends E> c) {
//先将集合c转换成对象 数组
Object[] a = c.toArray();
//数组的长度
int numNew = a.length;
//检查是否需要扩容,容量为当前列表长度+集合列表长度
ensureCapacityInternal(size + numNew); // Increments modCount
//复制数组到列表尾
System.arraycopy(a, 0, elementData, size, numNew);
//改变size大小
size += numNew;
//如果集合不为空,返回追加成功
return numNew != 0;
}
/**
* 添加元素集到指定位置,该位置及后面元素后移
*
*
*
* */
public boolean addAll(int index, Collection<? extends E> c) {
//添加范围检查
rangeCheckForAdd(index);
//元素集转成对象数组
Object[] a = c.toArray();
//元素集个数
int numNew = a.length;
//内部扩容检查
ensureCapacityInternal(size + numNew); // Increments modCount
//待移动元素个数
int numMoved = size - index;
//将index及之后的元素元素后移numMoved位
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将元素插入列表中
System.arraycopy(a, 0, elementData, index, numNew);
//列表长度改变
size += numNew;
return numNew != 0;
}
/**
* 范围检查
*
* */
private void rangeCheck(int index) {
//如果当前位置下标大于列表长度,抛出outOfBoundsMsg异常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* 添加元素到指定下标处时,范围检查
* */
private void rangeCheckForAdd(int index) {
//下标不合法时,抛出outOfBoundsMsg异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.6删除
ArrayList提供如下几个删除方法:
1.public E remove(int index);移除指定位置处的元素,并返回该移除的元素值;
2.public boolean remove(Object o);移除首次出现的元素;
3.public boolean removeAll(Collection<?> c);移除列表中与元素集相同的所有元素;
4.public boolean retainAll(Collection<?> c);移除列表中与元素集不同的所有元素;
/**
* 移除指定位置处的元素,并返回该移除的元素值
*
* */
public E remove(int index) {
//范围检查
rangeCheck(index);
modCount++;
//得到该位置处的元素
E oldValue = elementData(index);
//需要移动的元素数
int numMoved = size - index - 1;
//需要移动的数大于0,则将钙元素后面的元素前移
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将最后一位置为空,size并减一
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
/**
* 移除第一次出现的元素Object
*
* */
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;
//从index+1处的元素前移
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
/**
* 移除列表中与元素集相同的所有元素
* */
public boolean removeAll(Collection<?> c) {
//检查元素集是否为空
Objects.requireNonNull(c);
return batchRemove(c, false);
}
/**
* 移除列表中与元素集不同的所有元素
*
* */
public boolean retainAll(Collection<?> c) {
//检查元素集是否为空
Objects.requireNonNull(c);
//移除元素
return batchRemove(c, true);
}
/**
* ?
* 移除元素
*
* */
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//判断是否包含该元素,complement为ture,则为相同元素,否则为不同元素
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}