浅谈ArraryList扩容机制
阅读本文大约需要10分钟,将分成两部分解读ArrayList的扩容机制,源码部分来源于JDK8。 首先,挖个坑:为什么要尽量指定集合大小? 集合初始化 集合初始化有两种方式,直接new,或者在new的时候指定集合大小 List list1 = new ArrayList(); List list2 = new ArrayList(10); 我们看看这两种方式的区别 显然,ArrayList本质是一个Object数组,使用无参构造时会为其分配一个空数组,而指定大小时会使用有参构造器new一个指定大小的数组 但是,可能有细心的同学发现了一个问题,那就是无参构造器的注释上明明写着默认容量为10, ArrayList是Java中常用的一种动态数组,它允许我们在列表中存储对象。本文主要探讨ArrayList的扩容机制,特别是为何在创建ArrayList时尽量指定集合大小。我们首先来看看ArrayList的两种初始化方式: 1. 直接使用无参数构造器: `List list1 = new ArrayList();` 这种方式创建的ArrayList内部会默认创建一个容量为10的Object数组。虽然无参构造器的注释上写明默认容量为10,但实际上初始集合的大小是0,数组内部为空。 2. 指定初始容量的构造器: `List list2 = new ArrayList(10);` 在这种情况下,ArrayList会直接创建一个指定大小的Object数组。 当我们向ArrayList中添加元素时,扩容机制开始发挥作用。ArrayList的`add()`方法会在数组末尾添加元素。为了确保添加操作不会导致索引越界,`ArrayList`内部有一个`ensureCapacityInternal()`方法,它调用`calculateCapacity()`来计算满足添加元素的最小容量。如果Object数组为空,`calculateCapacity()`会返回10(因为`DEFAULT_CAPACITY`常量值为10)。这就是为什么即使没有指定容量,ArrayList在添加第一个元素时会自动扩容到10。 接下来,`ensureExplicitCapacity()`方法确保当前容量足够。如果最小容量大于现有容量,ArrayList会进行扩容。扩容的新容量通常是旧容量的1.5倍左右。例如,如果旧容量是10,那么新容量将是15。这个1.5倍的因子是为了平衡空间效率和扩容操作的频率。如果新容量小于计算出的最小容量,那么新容量会被设置为最小容量。之后,ArrayList会通过`Arrays.copyOf()`创建一个新的数组,并使用`System.arraycopy()`将旧数组的内容复制到新数组中,完成扩容。 了解这个机制后,我们可以理解为何建议在创建ArrayList时指定集合大小。如果你知道要存储的元素数量,预先指定容量可以避免不必要的扩容操作,从而提高性能。例如,如果需要存储11个元素,直接指定容量为11可以避免在添加第11个元素时触发扩容,因为每次扩容都会涉及到新数组的创建和复制,这对性能有一定的影响。 总结来说,ArrayList的扩容机制是通过计算最小容量和扩展当前容量的一定比例来实现的,初始容量默认为10。在使用ArrayList时,根据预期的元素数量预设容量可以有效减少不必要的扩容操作,优化性能。在实际编程中,合理预估并指定集合容量是一项重要的性能优化技巧。