0
点赞
收藏
分享

微信扫一扫

来聊聊ArrayList是如何动态扩容的,是一种怎样的机制?

ArrayList源码解析整体分析

ArrayList与LinkedList对比

文章目录

1)扩容代码

以上两篇文章,详情直接点击即可,本篇则主要分析一下扩容机制,ArrayList到底是如何扩容的?


    /**
     * Default initial capacity.
     * 默认初始容量
     */
    private static final int DEFAULT_CAPACITY = 10;
 private void ensureCapacityInternal(int minCapacity) {
 		//判断初始化的elementData是不是空的数组,也就是没有长度
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //Math.max得出最大值,DEFAULT_CAPACITY=10;
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
		
        ensureExplicitCapacity(minCapacity);
    }

 /**
 *判断当前ArrayList是否需要进行扩容
 **/
 private void ensureExplicitCapacity(int minCapacity) {
        //快速报错机制
        modCount++;

        //minCapacity如果大于了实际elementData的长度,那么就说明elementData数组的长度不够用,不够用那么就要增加elementData的length
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

2)minCapacity真正的理解

什么时候走真正的扩容?那肯定是minCapacity - elementData.length > 0,也就是说明elementData本身的长度不够用了。
minCapacity大小直接跟add调用有关系。有两种情况:

  1. add(E e)调用的时候,minCapacity=size + 1,第一次add的时候,也就是minCapacity=1,判定初始值为10,所以最终Math.max得到minCapacity大小也为10;
  2. add(int index, E element) 调用有参构造初始化‘添加元素后所需的最小数组容量’为添加元素后实际元素个数

3)grow核心扩容逻辑

//最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//增加容量以确保它至少可以容纳最小容量参数指定的元素数量。
//参数:minCapacity – 所需的最小容量
private void grow(int minCapacity) {
        //赋值之前的容量大小
        int oldCapacity = elementData.length;
        //新的容量大小=之前容量大小1.5倍
        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:
        //Arrays.copyOf空间换时间的扩容策略
       
        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;
    }

调用Arrays.copyOf方法将elementData数组指向新的内存空间时newCapacity的连续空间
并将elementData的数据复制到新的内存空间

4)为什么ArrayList的最大数组大小为Integer.MAX_VALUE-8?

参考文章:请点击这里

2^31 = 2,147,483,648 ,作为自己需要8 bytes存储大小 的数组2,147,483,648

5)代码调试,验证扩容逻辑

我们之前写过一篇关于反射的文章:
如何使用反射获取属性值
对elementData我们也是通过反射来获取。

测试demo如下:

public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		// 定义一个数组,训话放入21个值,看如何动态扩容?
		for (int i = 0; i < 21; i++) {
			list.add(i);
			// 没增加一个元素,都通过反射机制获取elementData值,并打印
			Class<ArrayList> arrayListClass = ArrayList.class;
			try {
				Field field = arrayListClass.getDeclaredField("elementData");
				field.setAccessible(true);
				Object[] objects = (Object[]) field.get(list);
				System.out.println("第" + i + "个元素扩容后的elementData: " + objects.length);
			} catch (NoSuchFieldException | IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

输出结果:

我们可以很清晰明了的看到扩容的情况

  • 当list大小<10,容量为默认值也就是10
  • 当10=<list<15,容量为1.5倍,也就是10*1.5=15
  • 当15=<list<21,容量又扩容15倍,也就是15*1.5=22.5,整数为22
举报

相关推荐

0 条评论