ArrayList底层原理(详细源码分析)

本文深入剖析了ArrayList的内部实现机制,包括其初始化过程、数组扩容原理、基本操作方法如add、remove、get和set等,并解释了ArrayList在不同场景下的性能表现。

ArrayList是一种基于定长数组实现的变长集合类,线程不安全,易查询,难增删

源码分析

ArrayList全局属性

定义了初始容量10

使用默认构造方法初始化出来的容量是 10(1.7 之后都是延迟初始化,即第一次调用 add 方法添加元素的时候才将elementData 容量初始化为 10)。

 private static final int DEFAULT_CAPACITY = 10;

定义了2个大小为0的数组

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

声明了一个数组

transient修饰不被序列化

transient Object[] elementData;

定义了size变量

标识数组大小

private int size;

ArrayList构造方法

ArrayList无参构造

将之前声明创建的大小为0的数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋给elementData数组

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ArrayList有参构造

传入一个int类型的变量,指定使用ArrayList的容量。如果传入的参数initialCapacity大于0,则新建一个initialCapacity大小的数组。传入参数值等于0的话,将之前创建的EMPTY_ELEMENTDATA空数组给elementData。

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方法

add方法

add方法中调用了ensureCapacityInternal方法  并将size+1作为参数传入

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

 ensureCapacityInternal方法

首先调用了calculateCapacity方法作为参数传入

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

calculateCapacity方法

判断当前数组是否为之前定义的空数组,

若为空则返回默认容量10和传入参数的最大值

若不为空则直接返回传入的数值,然后返回ensureExplicitCapacity方法

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

ensureExplicitCapacity方法

modCount是ArrayList继承抽象父类AbstractList中的一条属性,初始为0

protected transient int modCount = 0;

 首先进行判断,如果传入的数组长度减去数组原先的长度大于0,我们则调用grow方法(扩容方法),并将新长度传入进去。

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

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

grow方法

这段代码就是扩容的核心,首先看前两行代码定义了旧长度和新长度

新长度=旧长度+旧长度>>1(右移运算) 等同于 新长度=旧长度+0.5旧长度=1.5旧长度,所以ArrayList底层数组每次扩容时都是先将原数组长度的1.5倍与传入的数组长度进行比较。

接下来进入if判断

第一个if,如果新长度小于传入的数组长度,那么就更新newCapacity(新长度)的值为minCapacity(传入长度)

第二个if,如果新数长度大于最大的数组长度,会进入hugeCapacity方法再次进行判断,返回Integer的最大长度,或者Integer的最大长度-8的长度。

最后通过Arrays.copyOf方法进行数组扩容

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);
    }

 hugeCapacity方法

Integer最大值

@Native public static final int   MAX_VALUE = 0x7fffffff;

arraylist数组长度最大值

为什么是Integer最大长度-8而不是-7或者-6呢?

数组类型是由jvm从元素类型合成出来的;在jvm中获取数组的长度是用arraylength这个专门的字节码指令的,在数组的对象头里有一个_length字段,记录数组长度,只需要去读_length字段,所以ArrayList中定义的最大长度为Integer最大值减8,这个8就是就是存了数组_length字段。Integer是int类型的包装类,而int类型在内存中占8bit

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

remove方法

删除是先判断删除的数组坐标是否越界,然后使用System.arraycopy进行数组复制,然后让旧的elementData最后一位置为null进行GC垃圾清理

public E remove(int index) {
        rangeCheck(index);//是判断数组坐标是否越界抛出异常的方法

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

get方法

get方法较为简单,判断是否越界后直接返回数组

public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

set方法

set方法也很简单,判断是否越界后,取旧值,再赋新值,最后返回旧值

public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值