ArrayList源码分析一

本文详细解析了ArrayList的内部实现,包括默认容量、空数组、成员变量和构造方法。重点关注了ArrayList的扩容策略,当元素添加导致容量不足时,ArrayList会进行1.5倍的扩容。此外,还介绍了最大数组长度及为何存在两个长度为0的静态常量数组。
摘要由CSDN通过智能技术生成

ArrayList是我们在开发过程中经常使用的一个集合类,所以我们有必要对其了解详细。

常量

DEFAULT_CAPACITY

集合的默认容,创建集合时,如果没有指定容量,在第一个元素添加进来时,会默认扩展为该容量大小。

    private static final int DEFAULT_CAPACITY = 10;

EMPTY_ELEMENTDATA

空数组,创建ArrayList实例时,如果指定容量为0,则默认使用该数组。

    private static final Object[] EMPTY_ELEMENTDATA = {};

DEFAULTCAPACITY_EMPTY_ELEMENTDATA

DEFAULTCAPACITY_EMPTY_ELEMENTDATA 也是空数组,创建Arraylist时,如果不指定容量,则默认使用该数组,由于EMPTY_ELEMENTDATA是用来创建指定容量为0的数组 ,为了区分到底使用哪种方式创建的数组,所以加了一个DEFAULTCAPACITY_EMPTY_ELEMENTDATA,之所以要区分的目的是需要确定第一个元素添加进来,数组要扩容的具体长度。

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

成员变量

elementData

ArrayList实际存储元素的地方。

transient Object[] elementData

size

ArrayList包含的元素个数。

private int size;

构造方法

ArrayList(int initialCapacity)

Arraylist有两个构造方法,带参数构造方法接受一个初始容量参数initialCapacity,如果这个初始容量大于0,就创建一个指定容量大小的Object数组,并将其赋值给elementData,如果初始容量等于0,就将EMPTY_ELEMENTDATA赋值给elementData,小于0就抛出异常IllegalArgumentException。

    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()

无参构造方法,会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData

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

add方法

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

ensureCapacityInternal方法是来确保内部容量,我们继续往下看这个方法。

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

首先来看这一个calculateCapacity计算容量的方法。

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

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)在上面的内容我们解释过,如果创建ArrayList的时候没有指定容量,则会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData ,因此,这一行代码的逻辑实际上就是判断集合创建时有没有指定容量。如果没有指定容量,就会返回Math.max(DEFAULT_CAPACITY, minCapacity);也就是10和size+1的最大值。
然后我们再来看grow(minCapacity),这个方法其实就是扩容的具体逻辑。

private void grow(int minCapacity) {
        // 将数组已有长度赋值给oldCapacity 
        int oldCapacity = elementData.length;
        //计算新容量,新容量等于老容量的长度+老容量长度的一半,也就是扩容1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新容量小于指定的容量minCapacity,将minCapacity赋值给新容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果新容量大于了最大数组长度,最大数组长度等于Integer最大值-8,之所以减8的原因是防止因为不同的虚拟机导致分配内存失败,因为有些虚拟机会在数组中保留一些标题字
        //   private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
        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);
    }

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

上述代码已经加了具体的注释,这里有几个重要的点,一个就是扩容的大小是每次扩容源数组长度的1.5倍

问题思考与回顾

  1. 创建集合时的默认容量为多少?
  2. 第一次往集合添加元素时,集合会扩容到多少?
  3. 集合扩容的策略是怎样的?
  4. 集合的最大长度是多少?
  5. 为什么要定义两个长度为0静态常量数组?

这里还有另外一个重要的变量modCount,它是从父类继承而来,下一期文章将会注重介绍这一变量的作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值