1、底层元素
/**
* 默认的数组容量
*/
private static final int DEFAULT_CAPACITY = 10;
//空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//容器的层是数组
transient Object[] elementData; // non-private to simplify nested class access
//记录实际的元素个数
private int size;
2、 add方法
//向容器中添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); //首先;确保数组容量时够的
elementData[size++] = e;
return true;
}
//看看 ensureCapacityInternal(size + 1); 中的方法
//确保底层数组的容量够
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {//判断数组是不是空;如果时空则首次分配大小为DEFAULT_CAPACITY(10)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);//进行数组容量的修改
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //modCount表示内部的修改次数,modCount++当然是增加修改的次数
if (minCapacity - elementData.length > 0)//需要的长度大于当前数组的长度,调用grow方法
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//右移动一位相当于除以2,所以newCapacity相当于oldCapacity的1.5倍数
if (newCapacity - minCapacity < 0)//如果扩展的1.5倍数还是小于minCapacity,就扩展为minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//如果新的数组容量newCapacity大于数组能容纳的最大元素个数 MAX_ARRAY_SIZE 2^{31}-1-8
newCapacity = hugeCapacity(minCapacity);
// 底层进行数组的拷贝实现扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
在add
中关键就是进行底层数组的扩容,其扩容的核心思路:
- 判断数组是不是空;如果时空则首次分配大小为DEFAULT_CAPACITY(10)
- 需要的长度大于当前数组的长度,调用grow方法
- 先尝试新的数组大小为原数组大小的1.5倍数;
- 如果
1.5
倍满足不了,则扩展为数组需要的大小 - 进行数组扩容
- 添加元素
3、remove方法
public E remove(int index) {
rangeCheck(index); //进行索引范围检查
modCount++; //修改次数增加1
E oldValue = elementData(index); //查找待删除的值
int numMoved = size - index - 1;//计算要移动的元素的个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 将size减1,同时释放引用以便对原对象被垃圾回收
return oldValue;
}
- 进行索引位置检查
- 从index往后的元素都往前移动一位,实际调用
System.arrayCopy
方法移动元素 - 将最后一个位置设为
null
,设置为null
后不再引用原来的对象,如果原来对象也不再被其他对象引用,就可以被垃圾回收
4、迭代
4.1、迭代的陷阱
关于迭代器,有一种常见的误用,就是在迭代的过程中间调用容器的删除方法。比如,要删除一个整数``
for(Integer i:list){
if(i.equals(2)){
list.remove(i);
}
} //会抛出 Exception in thread "main" java.util.ConcurrentModificationException
发生了并发修改异常,为什么呢?
因为迭代器内部会维护一些索引位置相关的数据,要求在迭代过程中,容器不能发生结构性变化,否则这些索引位置就失效了。
所谓结构性变化就是
添加
、插入
,删除
元素,知识修改元素内容不算结构性变化
4.2、使用迭代器正确的删除
4.3、ArrayList中内部的Iterator
public Iterator<E> iterator() {
return new Itr();
}
//返回迭代器
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回的元素位置
int lastRet = -1; // 最后要返回元素的索引位置
int expectedModCount = modCount;
expectedModCount
表示期望的修改次数,初始化外部类当前的修改次数modCount
;
每次发生结构性变化的时候modCount
都会增加,而每次迭代器操作都会检查expectedCount
是否与modCount
相同,这样就能检测出结构性变化
查看hasNext
public boolean hasNext() {
return cursor != size;
}
查看Next
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();//检查是否发生结构性变化
int i = cursor; //获取当前指针的位置
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;//当前指针后移动
return (E) elementData[lastRet = i];
}
remove方法
public void remove() {//
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();//检查是否结构性
try {
ArrayList.this.remove(lastRet);//调用ArrayList的remove方法
cursor = lastRet; //更新cursor,lastRet
lastRet = -1;//更新expectedModCount的值
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
它调用了ArrayList的remove方法,但同时更新了cursor
、lastRet
和expectedModCount
的值,所以他可以正确删除
5、ArrayList实现的接口
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
AbstractList
List
RandomAccess
Cloneable
serializable
RandomAccess接口
表示可以随机访问;可随机访问就是举杯类似数组那样的特性,数据在内存是连续存放的,根据索引值就可以直接定位到具体的元素,访问效率高
6、其他方法
6.1、构造器
ArrayList(int initCapacity)
:指定的大小initalCapacity
初始化内部的数组大小
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
c ArrayList()
:构建空的
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
ArrayList(Collection<? extends E> c)
:以一个已有的Collection
构建,数据会新复制一份
6.2、返回数组
Object[] toArray()
: 返回Object数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public <T> T[] toArray(T[] a)
:返回对应类型的数组,如果参数数组长度足以容纳所有元素,就是用该数组,否则就创建一个新数组
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;
}