底层数据结构
ArrayList
的底层数据结构是数组
transient Object[] elementData;//底层数据结构,数组
private int size;//已存放数量
这里先说一下ArrayList
中大量使用的复制数组的方法,该方法是一个native
方法,jvm
进行了特殊的优化,比Java for循环复制效率高
public static native void arraycopy(Object src, // 源数组
int srcPos, //开始复制的位置
Object dest, //目标数组
int destPos, //复制到目标数组的开始位置
int length); //复制的个数
构造函数
使用无参构造函数时,底层数组指向默认的空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//{}
}
按容量初始化数组
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);
}
}
复制已有集合,参数为null
会报NullPointerException
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
add(E e)
add(E e)
是ArrayList
的核心方法之一,该方法把元素插入List
末尾,实现:
- 保证数组能容纳新元素,在数组不能容纳新元素时进行扩容
- 把元素插入末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
扩容的核心代码
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// = oldCapacity + oldCapacity/2 = oldCapacity * 1.5,扩容后容量为原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 把数据复制到新数组里,最终调用了 System.arraycopy();
elementData = Arrays.copyOf(elementData, newCapacity);
}
add(int index, E e)
在指定位置插入元素,实现
- 检查
index
- 必要时扩容
- 把
index
之后的元素后移,在index
处插入元素
public void add(int index, E element) {
rangeCheckForAdd(index);//检查index
ensureCapacityInternal(size + 1); // 必要时扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//把index之后的元素后移
elementData[index] = element;//在index处插入元素
size++;
}
示意图
remove(int index)
删除指定位置的元素,实现:
- 检查
index
, - 把
index
之后的元素前移,index
上的元素被覆盖,把删除后一个元素(最后一个元素重复了)
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;
return oldValue;
}
示意图
ConcurrentModificationException
运行以下代码,会出现异常 java.util.ConcurrentModificationException
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (String s : list) {
list.remove(s);
}
}
这是因为foreach
是一个语法糖,真正的内部实现为
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
String s = it.next();
list.remove(s);
}
而在使用Iterator
时不能使用Iterator.remove()
Iterator.add(obj)
之外的方法对list
进行增删,否则就会抛出ConcurrentModificationException
正确写法
- 使用
Iterator.remove()
-
使用 Java8 新增方法
removeIf()
default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed; }
subList()
运行这段代码会出现异常ClassCastException
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
ArrayList<String> arrayList = (ArrayList<String>) list.subList(0, 1);
}
这里的原因《阿里巴巴 Java 开发手册》也说得很清楚了
- 【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常,即 java.util.ArrayList$SubList cannot be cast to java.util.ArrayList。
说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上- 【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、 增加、删除产生 ConcurrentModificationException 异常。
小结
-
ArrayList
底层数据结构是数组 - 插入删除需要移动元素(末尾除外)
- 扩容后容量为原来的1.5倍
ConcurrentModificationException
subList
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。