ArrayList扩容方式的理解

ArrayList是Java中实现List接口的一个类,基于Object数组实现,支持自动扩容。其查询性能优于LinkedList,但插入删除效率较低。当添加元素导致容量不足时,ArrayList会扩容为原容量的1.5倍或直接扩大到满足需求的容量,避免频繁扩容。在遍历时推荐使用for循环,因为其实现了RandomAccess接口。
摘要由CSDN通过智能技术生成

1. ArrayList简介

ArrayList 是List接口的大小可变数组的实现,利用grow方法实现自动扩容;

ArrayList 本质上是一个Object数组,允许存储null值;

ArrayList 实现了RandomAccess,所以在遍历它的时候推荐使用for循环,其iterator和listIterator方法返回的迭代器是快速失败的;

ArrayList 查询性能高于LinkedList,而插入删除性能不如LinkedList;

 2. ArrayList 继承关系

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

3. ArrayList 数据结构

// 序列化id
private static final long serialVersionUID = 8683452581122892189L;

// 默认的初始化容量
private static final int DEFAULT_CAPACITY = 10;

//指定该ArrayList容量为0时,返回该空数组。
private static final Object[] EMPTY_ELEMENTDATA = {};

//当调用无参构造方法,返回的是该数组。刚创建一个ArrayList 时,其内数据量为0。与EMPTY_ELEMENTDATA的区别就是:该数组是默认返回的,而EMPTY_ELEMENTDATA是在用户指定容量为0时返回。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//保存添加到ArrayList中的元素。 
//该值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时,当第一次添加元素进入ArrayList中时,数组将扩容值DEFAULT_CAPACITY。 
//被标记为transient,在对象被序列化的时候不会被序列化。
transient Object[] elementData;

//ArrayList的实际大小,默认为0
private int size;

//分派给arrays的最大容量
//减8是因为某些VM会在数组中保留一些头字,尝试分配这个最大存储容量可能会导致array容量大于VM的limit,最终导致OutOfMemoryError。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

elementData是由transient修饰的,表示序列化时忽略该属性,但是我们知道ArrayList肯定是可以进行序列化的,这是由于ArrayList的writeObject和readObject方法

 add方法过程:

  • 检查ArrayList的容量,来决定是否扩容;
  • 在elementData[size++]处赋值

calculateCapacity 主要应用于无参构造时;

ensureExplicitCapacity 判断最小容量是否大于数组缓冲区当前长度,若大于则扩容;

//将指定的元素追加到此列表的末尾
    public boolean add(E e) {
        //确认list容量,如果不够,容量加1
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
    }

    //数组容量检查,不够时则进行扩容
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 取minCapacity为DEFAULT_CAPACITY和参数minCapacity之间的最大值。DEFAULT_CAPACITY默认是10。
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    //数组容量检查,不够时则进行扩容
    // minCapacity 想要的最小容量
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //最小容量>数组缓冲区当前长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//扩容
    }

grow方法过程:

  • 确定新容量newCapacity;
  • 利用Arrays.copyOf(elementData, newCapacity)方法改变容量大小;

 

//扩容,保证ArrayList至少能存储minCapacity个元素 
    //第一次扩容,在原有的容量基础上增加一半,如果容量还是小于 minCapacity,就将容量扩充为 minCapacity。
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 当前容量增加1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果扩容后的容量还是小于想要的最小容量
        if (newCapacity - minCapacity < 0)
            //将扩容后的容量再次扩容为想要的最小容量
            newCapacity = minCapacity;
        //如果扩容后的容量大于临界值,则进行大容量分配
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //新的容量大小已经确定好了,就copy数组,改变容量大小。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    //进行大容量分配
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0)  throw new OutOfMemoryError();
        //如果想要的容量大于MAX_ARRAY_SIZE,则分配Integer.MAX_VALUE,否则分配MAX_ARRAY_SIZE   
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;

 4. 总结:

ArrayList 是利用Object数组实现容量大小动态可变,扩容机制为首先扩容为原始容量的 1.5 倍。如果1.5倍太小的话,则将我们所需的容量大小赋值给 newCapacity,如果1.5倍太大或者我们需要的容量太大,那就直接拿 newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE 来扩容。 扩容是通过数组的拷贝,所以尽可能减少扩容操作。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

说好陪我数星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值