集合深度学习03—ArrayList核心源码

这篇博客详细解析了ArrayList在JDK1.7和1.8中的实现原理。在1.7中,ArrayList初始化长度为10,扩容时按1.5倍增长。1.8则在构造时使用空数组,首次add时才分配长度为10的新数组,以节省内存。博客讨论了添加元素、扩容机制及其优化,并对比了两个版本的差异。
摘要由CSDN通过智能技术生成

一、ArrayList实现类JDK1.7源码

1. 构造方法

调用无参构造器,调用本类带参构造器,初始值为10

在这里插入图片描述

transient:不可序列化。
Object类型的数组,所有 list 可以存任意类型,因为是Object,所有类的父类。
size:数组中的有效数据。

在这里插入图片描述

不就相当于 Object[ ] elementDates = new Objectp[10]
数组初始化,长度为10。

在这里插入图片描述

对应内存。
size为0是还没有 有效元素。
在这里插入图片描述

2. 添加元素

在当前有效位末尾加一个元素
有效位++

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

指定位置添加元素

    public void add(int index, E element) {
    	// 范围检查
        rangeCheckForAdd(index);
        //自动扩容机制
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
    
    private void rangeCheckForAdd(int index) {
    //只可以在 [ 0 , size ] 里选位置
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

3. 放满了,装不下了,就得扩容了

新手小伙伴会问 list 怎么会放满呢?
ArrayList 底层是数组啊,数组有大小。只是自动扩容,不深究看不到。
LinkedList底层是链表,不会满

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

内容保证能力
如果超出当前数组长度,就要扩容,并且把这个 minCapacity值 传入

在这里插入图片描述

  1. newCapacity=oldCapacity+(oldCapacity>>1)
    右移,就是除 2
    所以新容量至少是原来的 1.5 倍
  2. 如果新容量还没有传来的容量大,则用传来的容量
  3. 如果新容量比int最大值-8还大

如果比int最大值还大,则用int最大值,否则用int最大值-8

  1. 复制原数组元素到新数组
  2. 老数组指向新数组

在这里插入图片描述

    /**
     * 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
     * 要分配的数组的最大大小。
     * 一些 VM 在数组中保留一些标题字。
     * 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超过 VM 限制
     * 所以尝试分配小一点的
     */
   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;
    }

二、ArrayList实现类JDK1.8源码

底层依旧是Object类型的数组
size依旧是有效长度

    transient Object[] elementData; // non-private to simplify nested class access

    private int size;

1.构造方法

JDK1.8数组初始化时为空数组,不再指定初始大小

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

2. 添加方法

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

确保内部容量

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

计算容量
如果是空数组,则取 当前容量 和 默认容量10 比较,取最大值

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

确保显示容量
如果计算出来的容量 比 当前实际容量大,就扩容

   private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

自动扩容

  1. newCapacity=oldCapacity+(oldCapacity>>1)
    右移,就是除 2
    所以新容量至少是原来的 1.5 倍
  2. 如果新容量还没有传来的容量大,则用传来的容量
  3. 如果新容量比int最大值-8还大

如果比int最大值还大,则用int最大值,否则用int最大值-8

  1. 复制原数组元素到新数组
  2. 老数组指向新数组
    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);
    }
    /**
     * 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
     * 要分配的数组的最大大小。
     * 一些 VM 在数组中保留一些标题字。
     * 尝试分配更大的数组可能会导致 OutOfMemoryError:请求的数组大小超过 VM 限制
     * 所以尝试分配小一点的
     */
    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;
    }
    

三、总结

JDK1.7时 底层是数组,在调用构造器的时候,数组长度初始化为10,扩容的时候为1.5倍。

JDK1.8时 底层还是数组,在调用构造器的时候,底层数组为 { },空数组,在调用add方法后,底层数组才重新赋值为新数组,新数组的长度为10

好处是节省内存,在add后才创建长度为10的数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值