ArrayList 源码分析
本质:就是动态数组,动态扩容
1.内存地址连续,使用之前必须要指定数组长度
2.可以通过下标访问的方式访问成员,查询效率高
3.增删操作会给系统带来性能消耗[保证数据下标越界的问题,需要动态扩容]
增删:总是要copy,移动元素,效率低
查询:通过下标访问,效率很高
一、构造器
// 了解重要的基本属性 -- 方便理解源码
// 默认的数组的长度-容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组 -
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空对象 first element is added.
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 集合中实际存储数据的数组对象
transient Object[] elementData;
// 集合中元素的个数
private int size;
// 无参构造器
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// this.elementData = {}
}
// 有参构造器
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 初始长度大于0 就创建一个指定大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// {}数组赋值给 this.elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
二、重要方法
先分析
add
方法之后,在看看其他方法,基本就差不多啦,重点关注ensureCapacityInternal
计算容量
add
+ensureCapacityInternal
// add(E e)
public boolean add(E e) {
// 确定容量 动态扩容 size 初始 0
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将要添加的元素 添加到数组中 elementData[0] = 1 --> size = 1
elementData[size++] = e;
return true;
}
// 核心 -- 动态扩容
// ensureCapacityInternal
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// calculateCapacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 10 1 return 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 5
return minCapacity;
}
//ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 增长 操作次数
// minCapacity 10
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// grow
private void grow(int minCapacity) { // 10
// overflow-conscious code
int oldCapacity = elementData.length; // 0
// newCapacity = 0
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// newCapacity = 10
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// {} {,,,,,,,,,} 返回一个新的数组 长度为10
elementData = Arrays.copyOf(elementData, newCapacity);
}
-
其他重要方法
// add(int index, E element) 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++; } // remove public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); // 获取要移动的元素的个数 {1,2,3,4,5,6,7,8,9} // 3 size=9 index=3 // {1,2,3,5,6,7,8,9,null} int numMoved = size - index - 1; // 5 if (numMoved > 0) // 源数组 开始下标 目标数组 开始下标 长度 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work // 删除的节点对应的信息 return oldValue; } public E get(int index) { // 检查下标是否合法 rangeCheck(index); // 通过下标获取数组对应的元素 return elementData(index); } public E set(int index, E element) { rangeCheck(index); // 检查下标 // 获取下标原来的值 E oldValue = elementData(index); elementData[index] = element; return oldValue; }
三、自己的理解
自己理解性的文字,以及需要掌握的重要代码
初始化:
new ArrayList( initialCapacity )
//存储数据的对象 this.elementData
// 无参构造区
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// 参数 0
this.elementData = EMPTY_ELEMENTDATA;
// 参数 大于 0
this.elementData = new Object[initialCapacity];
add(E e){
/**
容量计算
ensureCapacityInternal
计算容量 calculateCapacity(size+1) //size 开始为 0 ,每次都会变化,等同于数组实际数据大小
- 无参 10以内 return 10 //默认给一个容量
大于10 return size+1
- 有参 return size+1
确保容量 ensureExplicitCapacity(size + 1)
大于数组容量:扩容--每次扩容到原来的1.5倍 -- 通过位运算 -- 第一次传多少是多少,第二次 才有可能出现扩容
最大容量不超过 Integer.MAX_VALUE
-- hugeCapacity
(minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; 新容量大小:newCapacity
对象变化:this.elementData = Arrays.copyOf(elementData, newCapacity);
**/
elementData[size++] = e;
}
add(index,e){
// add 只能是替换某个位置的元素,因为必须是 index < size
// rangeCheckForAdd
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
// arraycopy
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 赋值
elementData[index] = element;
}
remove(index){
// arraycopy
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// index 位置 给 null -- GC
elementData[--size] = null; // clear to let GC do its work
}
// get 就是数组通过index操作
get(index){
return elementData[index];
}
// set 就是数组通过index操作
set(index){
elementData[index] = element;
}
四、用例
// 会抛出异常
List arr = new ArrayList<>(5);
arr.add(3,1);
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 0
五、补充:FailFast机制
快速失败的机制,Java集合类为了应对并发访问在集合迭代过程中,内部结构发生变化的一种防护措施,这种错误检查的机制为这种可能发生错误通过抛出 java.util.ConcurrentModificationException
说白了就是list不能一边遍历一边修改
// ArrayList有个内部类Itr实现了Iterator接口
// ArrayList的每次操作modCount都会增加,如果另一个线程也做了修改,
// 可能会导致modCount不等于expectedModCoun
//next()
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
/**
* 解决方式:
* 使用Iterator的remove()方法
* 使用for循环正序遍历 - 需要修正index ,不然会漏掉个别元素
* 使用for循环倒序遍历
*/
// 不能使用增强for循环-因为使用的就是内部迭代器iteratorhasNext和next()方法来判断和取下一个元素。