1. ArrayList 简介
ArrayList
的底层是数组队列,相当于动态数组,它的容量能动态增长,线程不安全。
ArrayList
继承于 AbstractList
,实现了 List
, RandomAccess , Cloneable
, java.io.Serializable 这些接口。
ArrayList扩容是1.5倍机制:初始是10,第一次扩容后就是10+5
arduino
/**
* 首次初始化时,集合容量是10
*/
private static final int DEFAULT_CAPACITY = 10;
2.ArrayList --增
实例化时是个没有容量的空数组,首次调用add方法,容量扩容成默认容量10。
scss
public boolean add(E e) {
//计数、扩容方法
ensureCapacityInternal(size + 1);
//设值
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//首次新增会进入此if,获取初始化容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 当真实数据数量大于容量时会触发下面扩容方法
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 暂存旧的容量
int oldCapacity = elementData.length;
// 计算新的容量:10+10>>1 = 15
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// MAX_ARRAY_SIZE这个数值很大,正常业务代码中,很难触发这个if,可以不关注
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 使用数组拷贝方法获得扩容后新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
3.ArrayList --删
java
// 根据值删除
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;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
//根据下标删除
public E remove(int index) {
//校验传入下标是否超过size(size代表数据具体数值数量)
rangeCheck(index);
modCount++;
//获取当前传入的下标对应元素值
E oldValue = elementData(index);
//复制的位数
int numMoved = size - index - 1;
//大于0才会移动数组,正好等于0时代表你删除的是最后一位数据,所以也不需要移动数组
if (numMoved > 0)
/**
* System.arraycopy的方法原型:调用此方法后结果就是index后的数据全部往前移动一位
* @param src 要复制的源数组
* @param srcPos 源数组要复制的起始位置(从0开始)
* @param dest 要复制的目标数组
* @param destPos 目标数组的起始位置(从0开始)
* @param length 要复制的长度
*/
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// clear to let GC do its work 数值置为null,断开强引用,等待gc回收
elementData[--size] = null;
return oldValue;
}
4.ArrayList --查
scss
public E get(int index) {
//校验传入下标是否超过size(size代表数据具体数值数量)
rangeCheck(index);
//根据下标获取数组中对应的值返回
return elementData(index);
}
5.ArrayList --非迭代器删除方法报错原因
php
for (String a : list) {
list.remove(a);
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at TestString.main(TestString.java:22)
遍历时会调用内部类Itr->next方法
arduino
final void checkForComodification() {
//此方法会校验modCount和expectedModCount一定要相同才能成功
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
而集合自带的删除方法,只会递增modCount,却不会更新expectedModCount,所以第二个元素删除时就会报错
我们需要调用迭代器里面的删除方法,就不会再报错,方法如下:
csharp
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//重要的是这句代码,会同步modCount值给expectedModCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}