ArrayList源码&扩容机制分析

发布时间 2023-04-17 23:01:22作者: 壹索007

ArrayList底层是数组队列,相当于动态数组。

  在增加大量元素前,应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量。

  ArrayList继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable接口。/

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
}

RandomAccess:一个标志接口,表明这个接口的List集合是支持快速随机访问的,可以通过元素序号快速获取元素对象。

Cloneable:覆盖了clone(),能被克隆。

java.io.Serializable:ArrayList支持序列化,能通过序列化去传输。

  序列化: 将数据结构或对象转换成二进制字节流的过程
  反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程

 

ArrayList和Vector的区别:

  ArrayList 是 List 的主要实现类,底层使用 Object[ ]存储,适用于频繁的查找工作,线程不安全 (ArrayList中任何方法都没有任何加锁操作,在多线程写时可能会出现值覆盖或者空指针异常等问题,有fail-fast iterable异常);

  Vector 是 List 的古老实现类,底层使用 Object[ ]存储,线程安全的(但Vector实现线程安全问题是通过给add()方法加上了synchronized,让其add方法变成一个同步方法。这样做虽然保证了线程安全,但牺牲了不小的性能)。

ArrayList与LinkedList的区别:

  1.是否保证线程安全:两个都是不同步的,不保证线程安全。

  2.底层数据结构:Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别)

  3.插入和删除是否受元素位置影响:

     ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。

    ② LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响,近似 O(1),如果是要在指定位置i插入和删除元素的话((add(int index, E element)) 时间复杂度近似为o(n))因为需要先移动到指定位置再插入。

  4.是否支持快速随机访问:ArrayList底层基于数组,支持快速随机访问。LinkedList不支持随机访问,访问非链表首尾得元素比较低效。

  5.内存空间占用:ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

ArrayList扩容机制:

//ArrayList的扩容机制
//ArrayList的扩容机制提高了性能,
//如果每次只扩充一个,
那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList的扩容机制避免了这种情况。 /** * 如有必要,增加此ArrayList实例的容量,以确保它至少能容纳元素的数量 * @param minCapacity 所需的最小容量 */ public void ensureCapacity(int minCapacity) { //如果是true,minExpand的值为0,如果是false,minExpand的值为10 int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; //如果最小容量大于已有的最大容量 if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } //1.得到最小扩容量 //2.通过最小容量扩容 private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 获取“默认的容量”和“传入参数”两者之间的最大值 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } //判断是否需要扩容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) //调用grow方法进行扩容,调用此方法代表已经开始扩容了 grow(minCapacity); } /** * 要分配的最大数组大小 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * ArrayList扩容的核心方法。 */ private void grow(int minCapacity) { // oldCapacity为旧容量,newCapacity为新容量 int oldCapacity = elementData.length; //将oldCapacity 右移一位,其效果相当于oldCapacity /2, //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍, int newCapacity = oldCapacity + (oldCapacity >> 1); //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量, if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //再检查新容量是否超出了ArrayList所定义的最大容量, //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE, //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。 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); } //比较minCapacity和 MAX_ARRAY_SIZE private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }