参数
代码语言:javascript复制 //默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//空数组(用于空实例)
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于默认大小空实例的共享空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//保存数据的数组
transient Object[] elementData; // non-private to simplify nested class access
//元素个数
private int size;
注意:这里有两个空数组,第一个空数组是容量为0的时候的数组,第二个空数组是使用空参构造器的时候的数组
构造方法
代码语言:javascript复制 //带有参数的构造器
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) {
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;
}
}
扩容方法
如果有必要增加此ArrayList实例的容量以确保它至少能容纳元素的数量
代码语言:javascript复制 public void ensureCapacity(int minCapacity) {
//这里就开始判处出前面两个空数组的区别了
//如果这个list是无参构造的化没那么minExpand就为10,否则为0
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
?
: DEFAULT_CAPACITY;
//如果最小容量大于已有的最大容量
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
如果最小所需容量大于了实际可存储容量那么就需要扩容
判断是否真的需要扩容
代码语言:javascript复制 private void ensureExplicitCapacity(int minCapacity) {
modCount ;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这里就是扩容的具体办法其中分为几个情况
最开始新容量等于旧容量的1.5倍
- 新容量大等于最小所需容量,那么最后的新容量就等于旧容量的1.5倍
- 新容量小于最小所需容量,那么最后的新容量就等于最小所需容量
- 新容量大于List中规定最大容量,判断最小所需容量是否大于了List中规定的最小容量,如果没有则新容量还是等于最小容量,如果大于了则新容量等于Integer的最大值
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
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) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
代码语言:javascript复制 private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
计算最小容量是否会等于10
代码语言:javascript复制 private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
增删改查方法
增删改查的方法都是十分简单的所以不过多赘述
但是要注意在增加和删除方法的每一次调用的时候都会使modCount
Iterator迭代器
在ArrayList内部有个Itr的内部类
代码语言:javascript复制private class Itr implements Iterator<E> {
//当前游标位置
int cursor;
//上一个游标位置
int lastRet = -1;
//修改次数
int expectedModCount = modCount;
Itr() {}
//是否还存在下一个
public boolean hasNext() {
return cursor != size;
}
@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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i ]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
快速失败
前面都说了modCount属性,然后迭代器哪里也使用到了所以这里就说一下它的作用,它的作用就是实现快速失败,每次迭代器遍历的时候都会去查询实际的modCount属性与迭代器中保存的modCount属性是否相同,如果不同那么就抛出异常,这就是快速失败
拓展:安全失败:安全失败使用的写时复制技术,这个迭代器中遍历的数据是复制的数据,所以对于原有数据的修改不会影响到复制的数据