秋招之路1:ArrayList的底层实现原理
ArrayList
概述
- ArrayList 是基于数组实现的,是一个动态数组
- ArrayList 不是线程安全的,只能在单线程环境下;多线程使用ArrayList,应该考虑Collections.synchronizedList(List l)和concurrent并发包下的CopyOnWriteArrayList类
- ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输;
实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问;
实现了Cloneable接口,能被克隆。
首先,我的length和size有点混
1.length属性是针对Java中的数组来说的,要求数组的长度可以用其length属性;
2.length()方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
3.java中的size()方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
elementData
transient Object[] elementData:
transient:用transient关键字标记的成员变量不参与序列化过程。
size
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial 这个是什么意思呢?说明一个序列化属性
*/
private int size;
构造方法
一共三种:
//指定初始大小的构造方法
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() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//使用集合
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //转换为数组,这个数组中的类型是根据c判断的,有可能不是Object[]
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class) //不是Oject[]时候,利用copyOf进行转换
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
常常出现的modCount,expectedModCount变量
这一变量常常和线程安全有关,是记录修改次数的两个变量
add方法
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) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//看看本次增加的值,是否大于elementData了,大于再增加
grow(minCapacity);
}
//调用下面的这个方法
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//每次成1.5倍的增长
if (newCapacity - minCapacity < 0)
//1.5倍都不够,就直接用minCapacity作为新容量
newCapacity = minCapacity;
//是否超过MAX_ARRAY_SIZE[一般虚拟机的防溢出的最大内存]
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);
}
add方法的另外一个重载
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++;
}
//作用就是: 如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。
主要涉及这个方法
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
代码解释:
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度
add的另一个重载
//从指定的位置开始,将指定collection中的所有元素插入到此列表中。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
get方法
public E get(int index) {
rangeCheck(index);
//检查是否越界
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);
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;
}
//移除指定元素:
remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。
fastRemove:基本是和remove(int index)一致,但是跳过了边界检查条件。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) { //java 判断null的方法。
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) { //java的.equals是判断是否是值相等
fastRemove(index);
return true;
}
}
return false;
}
//removeRange
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work //后面的null元素,让gc去回收。
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
toArray方法
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//这个toArray方法,主要是用于array转换成特定的比如是String[]等数组时候用的
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
链接:toArray详解
Fail-Fast机制:
ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。
具体参见HashMap的实现原理 中的Fail-Fast机制。