ArrayList底层实现原理

本文详细解析了ArrayList在Java中的底层实现,重点介绍了数组容量的初始化、添加元素时的扩容检查和实际扩容过程,以及如何处理基本数据类型和引用类型数据的存储。
摘要由CSDN通过智能技术生成

前言

我们都知道ArrayList集合只能存储引用类型数据(Long、Short、Integer、Double、Float是引用类型),数组两者都可以存储(基本数据类型:long、short、int、double、float);ArrayList长度可变,而数组长度不可变。
但实际上集合ArrayList底层也是由数组实现的,这里我们来开始研究底层实现原理。

一.底层分析

    /**
     * 用于默认大小的空实例的共享空数组实例。
     * 我们将其与EMPTY_ELEMENTDATA区分开来,以了解何时膨胀多少添加第一个元素。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储ArrayList元素的数组缓冲区。
     * ArrayList的容量就是这个数组缓冲区的长度。
     * 任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
     * 将在添加第一个元素时扩展为DEFAULT_CAPACITY。
     */
    // Android-note: Also accessed from java.util.Collections
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 数组列表的大小(包含的元素数量)。
     *
     * @serial
     */
    private int size;
    
    /**
     * 构造一个初始容量为10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

先看构造方法,从ArrayList空参数构造方法中可以看出ArrayList创建的时候整了一个空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),也就是ArrayList创建时,数组初始长度为0。

    /**
     * 将指定的元素追加到此列表的末尾。
     *
     * @param e 元素添加到此列表
     * @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;
    }

add(E e)方法中调用(elementData[size++] = e)添加元素前,需要先调用ensureCapacityInternal(size + 1),进行扩容校验。

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 数组初始容量是这里确定的
            // 如果调用add添加单个元素,数组初始容量取DEFAULT_CAPACITY=10。
            // 如果调用addAll添加一个集合元素,比较集合长度和DEFAULT_CAPACITY,数组初始容量取最大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal方法中
elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA如果为true,初始数组是空,表示第一次添加元素。
数组初始容量是这里确定的,如果调用add添加单个元素,minCapacity必然小于DEFAULT_CAPACITY,数组初始容量取DEFAULT_CAPACITY=10;
如果调用addAll添加一个集合元素,比较集合长度和DEFAULT_CAPACITY,数组初始容量取其中最大值。

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        // 数组中已有元素数量+1是否大于当前数组长度
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * 要分配的数组的最大大小。
     * 有些虚拟机在数组中保留一些头字。
     * 尝试分配更大的数组可能会导致
     * OutOfMemoryError:请求的数组大小超过虚拟机限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 增加容量,以确保它至少可以容纳由最小容量参数指定的元素数量。
     *
     * @param minCapacity 所需的最小容量
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

ensureExplicitCapacity(int minCapacity) 方法主要校验是否扩容
每次添加元素前,都会判断if (minCapacity - elementData.length > 0)
检测数组中已有元素size+新增元素数量是否大于当前数组长度,大于表示数组元素已满,需要进行扩容。
(注:通过add方法添加元素,新增元素数量 则为1;使用addAll(Collection<? extends E> c)新增元素,新增元素数量 为集合c的长度)

grow(int minCapacity)方法开始进行具体扩容操作:

  1. 第一次添加 (调用add(E e)) 元素
    数组初始长度elementData.length为0,minCapacity为10,newCapacity -
    minCapacity < 0
    为true,newCapacity 赋值为minCapacity,得到新数组容量newCapacity
    为10, 最后通过**Arrays.copyOf(elementData, newCapacity)**创建新数组并原数组的元素拷贝到新数组中。

  2. 后面每次再添加元素前,都会检测数组中已有元素数量+1是否大于当前数组长度(minCapacity -elememntData.length>0 等于size+1-elememntData.length>0);
    如果是,计算新数组容量为原数组容量的1.5倍:
    int newCapacity =oldCapacity + (oldCapacity >> 1)
    创建一个容量为原数组容量的1.5倍的新数组,并将将原数组的元素拷贝到新数组中:
    Arrays.copyOf(elementData,newCapacity

新数组容量具体计算

int newCapacity = oldCapacity + (oldCapacity >> 1)
其中oldCapacity >> 1,原数组容量oldCapacity=10的时候,位移运算如下图
在这里插入图片描述
oldCapacity=10向右位移1得出结果为5,oldCapacity + oldCapacity>>5 = 15,也就是1.5倍。

二.总结:

ArrayList底层是由数组实现的,第一次添加元素前,会创建一个初始容量为 10的数组。
后面每次再添加元素前,都会检测数组中已有元素数量+新增元素数量是否大于当前数组长度,如果是,创建一个容量为原数组容量的1.5倍的新数组,将原数组的元素拷贝到新数组中。

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值