ArrayList扩容方式的理解

目录

1、成员变量

2、构造方法

3、ArrayList的扩容机制

4、总结


        ArrayList类又称动态数组,同时实现了Collection和List接口,其内部数据结构由数组实现,因此可对容器内元素实现快速随机访问。但因为ArrayList中插入或删除一个元素需要移动其他元素,所以不适合在插入和删除操作频繁的场景下使用。

在说明扩容方式之前,我们需要提前知道ArrayList内部封装的成员变量以及构造方法

1、成员变量

private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData;

private int size;

(1)DEFAULT_CAPACITY:这是一个常量,表示默认的容量大小,也是在没有初始化容量大小的时候 调用add方法后初始化的容量大小;

(2)EMPTY_ELEMENTDATA:定义空数组,大小固定为0;

(3)DEFAULTCAPACITY_EMPTY_ELEMENTDATA:定义默认空容量的数组,大小固定为0,将它于EMPTY_ELEMENTDATA数组区分开;

(4)elementData:实际存储元素的数组,并且不可以被序列化;

(5)size:表示数组元素的个数;

2、构造方法

(1)带有一个int类型参数的构造函数

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

        传入的int类型的参数表示数组容量的大小,根据传入的参数大小创建对应容量的数组

        当initialCapacity > 0时,会在堆上new一个大小为initialCapacity的数组,然后将其引用赋给elementData,此时ArrayList的容量为initialCapacity,元素个数size为默认值0。

  当initialCapacity = 0时,elementData被赋予了默认空数组,因为其被final修饰了,所以此时ArrayList的容量为0,元素个数size为默认值0。

  当initialCapacity < 0时,会抛出异常。
(2)无参构造函数

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

        当我们直接创建ArrayList的时候,将elementData赋予了一个默认空容量的数组,并且这个空容量的数组是被final关键词修饰的,所以说此时elementData是空的,元素个数和容量都为0;

        在调用add()方法添加元素的时候,会将默认的数组容量DEFAULT_CAPACITY分配给elementData;

(3)带有一个Collection对象参数的构造方法

public ArrayList(Collection<? extends E> c) {
		elementData = c.toArray();
		if ((size = elementData.length) != 0) {
			// c.toArray might (incorrectly) not return Object[] (see 6260652)
			if (elementData.getClass() != Object[].class)
				elementData = Arrays.copyOf(elementData, size, Object[].class);
		} else {
			// replace with empty array.
			this.elementData = EMPTY_ELEMENTDATA;
		}
	}

        传入Collection元素列表后,构造方法首先会将其转化为数组,将其索引赋给elementData。

  如果此数组的长度为0,会重新赋予elementData为空数组,此时ArrayList的容量是0,元素个数size为0。

  如果此数组的长度大于0,会更新size的大小为其长度,也就是元素的个数,然后执行里面的程序。大家对里面的代码可能不理解,让我们等会看下面解析。执行完后此时ArrayList的容量为传入序列的长度,也就是size的大小,同时元素个数也为size,也就是说,此时ArrayList是满的。

当我们了解了相关的成员变量和构造方法后,就可以深入的了解ArrayList的扩容机制

3、ArrayList的扩容机制

(1)首先我们需要从add()方法观察

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

        当我们第一次调用add()方法的时候,发现它里面调用了另一个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);
	}

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

        1、ensureCapacityInternal方法分别调用calculateCapacity和ensureExplicitCapacity函数 进行扩容;

        2、calculateCapacity方法用来判断elementData是否为空数组,也就是看数组是否添加了元素,如果是则返回DEFAULT_CAPACITY和minCapacity的最大值 否则直接返回minCapacity;

        3、ensureExplicitCapacity方法时用来判断minCapacity的elementData数组的长度大小,当minCapacity大于数组的长度的时候则调用扩容函数进行扩容;

        下面扩容开始,grow()表示扩容函数。

        其中定义的MAX_ARRAY_SIZE表示一个常量,防止数组的容量溢出 要分配的数组的最大大小

        hugeCapacity方法的作用是比较minCapacity和MAX_ARRAY_SIZE的大小 如果minCapacity小于0 则抛出异常;如果大于MAX_ARRAY_SIZE则返回Integer.MAX_VALUE ,小于MAX_ARRAY_SIZE则直接返回MAX_ARRAY_SIZE

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

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、oldCapacity用来记录旧数组的容量,newCapacity就是扩容后的容量,自动扩容为原容量的1.5倍;

        2、判断新容量的大小是否小于最小需要容量;

        3、判断当前新容量大于MAX_ARRAY_SIZE,使用hugeCapacity函数比较;

        4、最后将原来数组的元素拷贝到扩容后的数组;

4、总结

ArrayList的扩容机制可以分为两种情况:

1、当数组的容量为0的时候扩容,并且三种构造方法的扩容各不相同

        (1)无参构造:默认的数组容量为0,当第一次调用add()方法添加元素的时候,会给数组分配一个默认大小的容量10,当添加的元素元素个数大于该容量的时候,正常扩容;

        (2)int类型参数的构造方法:传入的参数既为数组容量的大小,当传入的容量大小为0的时候,在添加第一个元素以后,容量变为1,在需要添加的时候需要进行扩容;

        (3)Collection类型参数的构造方法:传入的Collection如果为空,那么数组容量为0,当添加第一个元素的时候,容量变为1,继续添加的时候就会发生扩容。

2、当数组的容量大于0的时候并且此时数组是满的,就进行正常扩容。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码农叮叮车

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

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

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

打赏作者

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

抵扣说明:

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

余额充值