一.集合与数组的区别
1.数组
- 静态的,固定数组长度(即创建之后无法改变),不会自动扩容
- 只能存储单一数据
- 增添删除比较麻烦,需要复制数组
2.集合
- 动态的,会自动扩容
- 可以存储不同数组(引用类型一般只能存储一种,支持泛型),也可以存储不同子类,但是声明得为该子类的父类
- 可以存储映射关系
3.ArrayList (源码浅赏析)
3.1 构造参数
- 底层是由数组实现
// 调用有参构造时 传入的参数为0 private static final Object[] EMPTY_ELEMENTDATA = {}; // 调用无参构造时 默认赋值 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 底层维护了一个Object[]的数组 transient Object[] elementData; // non-private to simplify nested class access // 有参构造 public ArrayList(int initialCapacity) { // 如果传入的参数>0 即一开始的数组容量 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { // 如果传入的 == 0 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } // 无参构造 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
3.2 扩容机制(重要)
(1)无参构造
由上图我们可知,底层会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,当我们第一次添加元素进去的时候,会调用
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
size为当前的List长度,该方法是用来判断是否需要扩容
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
由上述可知,这里会直接返回DEFAULT_CAPACITY,观阅源码可知,该值为10
第一次add时候 minCapacity为10 很明显是大于elementData.length的
所以初始化为10
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
结论:
ArrayList无参构造时,第一次添加元素,会默认赋值长度为10的数组
(2)有参构造
这里我们分为两种情况 initialCapacity = 0 和 initialCapacity > 0
我们先讲initialCapacity = 0的情况
同样,由第一张图我们可知,底层会将EMPTY_ELEMENTDATA赋值给我们的elementdata[]
同样我们添加元素的时候,即调用add()的时候,也会判断是否扩容
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
当前size为0 所以我们传进去的值为1
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
注意:这里elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA才会扩容为10
很明显这里我们并不是,所以这里的minCapacity为1
同样我们也会进行扩容操作(这里我就直接放扩容部分源码了)
这里我们判断 newCapacity - minCapacity < 0 所以newCapacity被赋值为1
结果就是扩容为1的数组
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); }
从上图我们可以看出,每次扩容List都是1.5倍的扩容,再进行copyOf的方法,从名字我们可以得知是将旧数组复制到新数组上。
------------------------------------------------------------------------------------------------------------------------
接下来是initialCapacity > 0的情况:假设我们new一个长度为3的ArrayList 底层实际上为
this.elementData = new Object[3];
添加元素的时候每次都会判断是否需要扩容 size此时为3
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
同样这里也不会走默认为10的数组中去 返回的 minCapacity为4
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } 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 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); }
最终扩容为长度为 3 + 3/2 = 4
4.注意
- ArrayList没有length()方法,也没有length这个属性,源码中的length,其实是elementData的length
- ArrayList只有size()来获取List中的长度。
本文借鉴:Java集合详解(含JDK8源码)_jdk8源码学习_汤姆&Tom的博客-CSDN博客
如果我理解的内容有误,烦请评论区留言或者私聊,欢迎各位来探讨~