首页 > 其他分享 >ArrayList扩容机制

ArrayList扩容机制

时间:2023-03-07 22:44:40浏览次数:52  
标签:扩容 int ArrayList elementData list minCapacity 机制 size

ArrayList扩容机制

ArrayList的底层实现是Object数组队列,相当于动态数组。它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。

transient Object[] elementData;

使用transient关键字修饰,代表不进行序列化操作(transient作用:java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。)

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

有源码可见:

​ ArrayList继承了AbstractList,实现了List接口,RandomAccess,Cloneable,Serializable接口。

  • RandomAccess一个标识,代表可以进行随机访问
  • Cloneable重写了clone方法,可以进行克隆。
  • 实现了Serializable接口,可以进行序列化操作

说到ArrayList就不得不听到LinkedList,虽然很少使用到。

区别:

  1. 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;

  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. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

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


以上区别总结来自:https://javaguide.cn/java/collection/arraylist-source-code.html

ArrayList 源码解读

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     	默认数组长度 10
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     	空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     	默认使用空数组
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     	数组
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     	list大小
     */
    private int size;

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     	有参构造函数,传入初始化大小
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { //大于0 直接初始化
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { //=0 空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 按照指定集合的迭代器返回元素的顺序,构造一个包含指定集合元素的列表。 参数: C -其元素将被放入此列表中的集合 抛出: NullPointerException -如果指定的集合为
     */
    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;
        }
    }

    /**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
     将此ArrayList实例的容量调整为列表的当前大小。应用程序可以使用此操作最小化ArrayList实例的存储空间。
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

    //这里开始扩容机制
    /**
     * 如果需要,增加这个ArrayList实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数量。 参数: minCapacity—所需的最小容量
     */
    public void ensureCapacity(int minCapacity) {
        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);
        }
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; //此列表在结构上被修改的次数。结构修改是指那些改变列表大小的修改,或者以某种方式扰乱列表,从而使正在进行的迭代可能产生不正确的结果。

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     	最大Array_size 留出8个
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        //获取旧数组长度
        int oldCapacity = elementData.length;
        //右移一位相当于 /2
        //即扩充为原来oldCapacity1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0) //如果扩容后还是小于最小容量
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) //如果大于最大的Array_size
            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;
    }

    /**
     * Returns the number of elements in this list.
     *
     * @return the number of elements in this list
     */
    public int size() {
        return size;
    }

    /**
     * Returns <tt>true</tt> if this list contains no elements.
     *
     * @return <tt>true</tt> if this list contains no elements
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * Returns <tt>true</tt> if this list contains the specified element.
     * More formally, returns <tt>true</tt> if and only if this list contains
     * at least one element <tt>e</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
     *
     * @param o element whose presence in this list is to be tested
     * @return <tt>true</tt> if this list contains the specified element
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
        /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

构造函数有三个,从无惨构造可以看出,默认分配的是空数组,当实际使用的时候才进行扩容的。

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

调用add方法时

ArrayList<Object> objects = new ArrayList<>();

objects.add(1);

进入add源码:

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    //调用ensureCapacityInternal,确保内部容量够用
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

进入ensureCapacityInternal()

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

调用了calculateCapacity方法,确认容量。

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //判断是否是默认空数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);//如果是,返回默认值10。
    }
    return minCapacity;//不是空数组,返回最小容量值
}

然后进入ensureExplicitCapacity方法

private void ensureExplicitCapacity(int minCapacity) {
    modCount++; //判断修改次数

    // 最小需要容量是否大于数组大小
    if (minCapacity - elementData.length > 0)
        grow(minCapacity); //扩容
}

扩容核心方法,grow方法

private void grow(int minCapacity) {
    // overflow-conscious code
    //获取旧容量大小
    int oldCapacity = elementData.length;
    //扩容至旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //判断是否满足最小容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;//不满足则直接赋值
    if (newCapacity - MAX_ARRAY_SIZE > 0) //判断是否超过MAX_ARRAY_SIZE
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);// 返回一个扩容数组
}

如果超过MAX_ARRAY_SIZE,调用hugeCapacity方法

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow,为负数,说明溢出了,抛异常
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? 
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

至此,扩容流程走完。

补充:System.arraycopy()Arrays.copyOf()方法

阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及add(int index, E element)toArray() 等方法中都用到了该方法!

两者联系和区别

联系:

看两者源代码可以发现 copyOf()内部实际调用了 System.arraycopy() 方法

区别:

arraycopy() 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置, copyOf() 是系统自动在内部新建一个数组,并返回该数组。

标签:扩容,int,ArrayList,elementData,list,minCapacity,机制,size
From: https://www.cnblogs.com/chengbb/p/17190016.html

相关文章

  • UI自动化--下拉框处理和等待机制
    UI自动化--下拉框处理和等待机制一、下拉框处理:网页上的js弹出框一般有三种情况,且识别不了元素,需要使用switch_to.alert()进行跳转这时候需要先跳转到此弹窗,才能做到自动化......
  • 6 异常机制
    异常机制1什么是异常软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。异常指程序运行中出现的不期而至的各种......
  • java断言机制(assert)
    java断言机制(assert)概述断言使用的时候不是很多,测试时会使用,springboot中也有使用,总的来说断言还是要慎重。在Java中,同样也有assert关键字,表示断言在Java中,assert关键......
  • RocketMQ高可用机制
    RocketMQ高可用机制集群部署模式1.单master模式2.多master模式配置配置文件broker.properties的brokerClusterName需要保持一致brokerId需要为0,0代表为0优缺点......
  • ArrayList和LinkedList的区别
    实现接口不同。两个都实现了List接口,LinkedList还实现了Deque接口。底层实现不同。ArrayList是基于数组实现,LinkedList是基于链表实现。效率存在差异。由于底层实现不同......
  • JVM内存回收机制
    JVM内存回收机制JVM内存回收机制标签:JVMGC垃圾回收内存管理  0.说明当JVM创建对象遇到内存不足的时候,JVM会自动触发垃圾回收garbagecollecting(简称GC)操作,......
  • MVCC 是什么?InnoDB 是如何实现 MVCC 机制的?
    概念MVCC全称Multi-VersionConcurrencyControl,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。解决问题MVCC主要解决的问题是可以保证MySQL在读的......
  • 大白话+画图 从源码角度一步步搞懂ArrayList和LinkedList的使用
    1.说说ArrayList1.基本原理ArrayList,原理就是底层基于数组来实现。01.基本原理:数组的长度是固定的,java里面数组都是定长数组,比如数组大小设置为100,此时你不停的往Arra......
  • ArrayList源码解读
    1.创建ArrayListList<String>list=newArrayList<>();list.add("wang");2.构造方法:elementData的长度就是ArrayList的容量,在第一次使用时,elementData的长度会扩展......
  • 多头自注意力机制实现及代码
    注意力机制是一种在给定文本词向量中查找重要词,并赋予一定重要权值的机制。假设输入序列为X,三个随机初始的矩阵键值K(Key)、查询值Q(Query)和值V(Value)。当Query、K......