集合框架图:
集合和数组的区别
AarrayList
ArrayList底层实现原理
ArrayList的底层实现是基于数组的动态扩容。
- 初始容量:当创建一个新的ArrayList对象时,它会分配一个初始容量为10的数组。这个初始容量可以根据需求进行调整。
//表示默认的初始容量,该值被设置为10
private static final int DEFAULT_CAPACITY = 10;
//表示一个空数组,即一个长度为0的Object数组。
private static final Object[] EMPTY_ELEMENTDATA = {};
//表示具有默认初始容量的空数组。在无参构造函数中使用这个数组。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//该数组用于存储ArrayList的元素。它是ArrayList的核心数据结构。
transient Object[] elementData;
//表示ArrayList当前包含的元素数量。
private int size;
//构造一个具有指定初始容量的ArrayList。如果指定的初始容量大于0,将创建一个具有此大小的新数组;如果初始容量为0,则使用空数组EMPTY_ELEMENTDATA;否则,抛出IllegalArgumentException异常。
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);
}
}
//构造一个默认初始容量的ArrayList。使用数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA作为初始数组。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 添加元素:当使用ArrayList的add(E element)方法添加一个元素时,它会先检查数组是否已满。如果数组已满,ArrayList会执行以下操作:
- 创建一个新的容量更大的数组,默认情况下是原数组容量的1.5倍(在Java 7之前是原数组容量的两倍)。例如,如果原数组的容量是10,那么新数组的容量将是15。
- 将原数组中的元素复制到新数组中。
- 更新ArrayList的内部数组引用为新数组。
- 更新ArrayList的内部数组引用为新数组。
private void grow(int minCapacity) {
//获取当前 ArrayList 的容量
int oldCapacity = elementData.length;
//计算新的容量,即将原始容量的一半加到原始容量上,相当于扩容 50%。
int newCapacity = oldCapacity + (oldCapacity >> 1);
//检查新容量是否小于所需的最小容量,如果是则将最小容量设为新容量。这个检查主要用于确保扩容后的容量足以容纳要添加的元素。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//检查新容量是否超过了 MAX_ARRAY_SIZE,如果是则调用 hugeCapacity(minCapacity) 方法获取一个巨大容量。MAX_ARRAY_SIZE 是 Java 数组的最大容量限制。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//将 elementData 数组扩容为新的容量。利用 Arrays.copyOf() 方法可以方便地将数组扩容并复制原始元素到新数组中。
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 删除元素:当使用ArrayList的remove(int index)方法删除一个元素时,ArrayList会执行以下操作:
- 将要删除的元素之后的所有元素向前移动一个位置,以覆盖被删除的元素。
- 更新ArrayList的内部数组长度。
public E remove(int index) {
//校验索引是否越界,如果索引值小于0或者大于等于当前列表的大小 size,则抛出 IndexOutOfBoundsException 异常。
rangeCheck(index);
//(用于记录结构性修改次数的计数器)加1,表示对 ArrayList 进行了一次修改。
modCount++;
//调用 elementData(index) 方法获取要删除的元素的值,将其保存为 oldValue。
E oldValue = elementData(index);
//计算需要移动的元素的个数,即从待删除元素的下一个位置到列表末尾的元素个数。
int numMoved = size - index - 1;
//检查是否需要移动元素。如果 numMoved 大于 0,表示待删除的元素不是列表的最后一个元素,需要将后面的元素往前移动,以填补删除元素的空缺。
if (numMoved > 0)
//使用 System.arraycopy() 方法将后面的元素往前移动。从 elementData 数组的 index+1 位置开始,复制 numMoved 个元素到 elementData 数组的 index 位置,覆盖待删除元素。
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//将列表中最后一个位置的元素设置为 null,以便让垃圾回收机制回收。同时,将列表的大小 size 减1,相当于删除了一个元素。
elementData[--size] = null;
//返回被删除的元素
return oldValue;
}
- 获取元素:当使用ArrayList的get(int index)方法获取一个元素时,ArrayList会直接通过索引访问数组中的对应元素。
E elementData(int index) {
return (E) elementData[index];
}
- 内存管理:当ArrayList执行扩容或缩容操作时,它会分配新的内存来存储更大或更小的数组,然后将旧数组中的元素复制到新数组中。这意味着在扩容或缩容过程中会涉及到内存的分配和复制操作,可能会带来一定的性能开销。