ArrayList源码分析
底层数据结构:Object类型数组:Object[ ] elementData
一、JDK 7主体源码
步骤及其源码底层实现
1.创建一个ArrayList对象
ArrayList list = new ArrayList(); //底层创建了长度是10的Object[] 数组elementData
底层实现:ArrayList.java
private transient Object[] elementData;
第一步:调用无参构造器
public ArrayList() {
this.(initialCapacity:10); //调用本类当中的重载构造器
}
第二步:this.(initialCapacity:10);调用本类当中的重载构造器
public ArrayList(int initialCapacity) {
super();
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity] //初始化数组,容量为10
}
2.添加数据,add()方法
list.add(1);
...
list.add(11); //elementData[0] = new Integer(1)
//如果此次的添加导致底层elementData数组容量不够,则扩容。
//默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中
底层实现:ArrayList.java
第一步:调用add( )
public boolean add(E e) {
ensureCapacityInternal(size + 1); //确认容量是否够 size:代表已经添加了几个,初次为0
elementData[size++] = e; //添加操作
return true;
}
第二步:调用ensureCapacityInternal(),确认容量是否够
private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //minCapacity即size + 1,elementData.length为10
grow(minCapacity); //扩容
}
- 扩容:当添加第11个元素时,size为10,size+1也就是minCapacity为11,此时if条件:minCapacity - elementData.length > 0
成立,于是调用grow()方法进行扩容。
grow()
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //初始容量:10
int newCapacity = oldCapacity + (oldCapacity >> 1); //默认扩容为原来的1.5倍,第一次扩容为15
...
elementData = Arrays.copyOf(elementData, newCapacity);
}
elementData = Arrays.copyOf(elementData, newCapacity);
原来容量为10的数组elementData需要被重新赋值为容量为15的数组,并且将原来的数组元素复制到新数组中
结论
建议开发中使用带参构造器ArrayList arrayList = new ArrayList(int capacity);这样可以避免扩容。
二、JDK 8中ArrayList的变化
1.创建一个ArrayList对象(没有创建长度是10的Object[] 数组)
ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{},并没有创建长度为10的数组
底层实现:ArrayList.java
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //指定了一个常量
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //这里什么都没有 长度为0
这里不像JDK 7那样在创建对象时就将数组实例化,从时间和占用内存(节省)上来说,这种方式较好一些
2.添加数据,add()方法
list.add(1) //第一次调用add()时,底层才创建了长度为10的数组,并将数据1添加到elementData[0]
...
底层实现:ArrayList.java
第一步:第一次调用add( )
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
第二步:调用ensureCapacityInternal(),确认容量是否够
0private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
第三步:调用calculateCapacity()
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //第一次调用add()时,该条件成立
return Math.max(DEFAULT_CAPACITY, minCapacity); //默认容量DEFAULT_CAPACITY为10
} //minCapacity即size+1=1
return minCapacity; //10
}
第四步:调用ensureExplicitCapacity()
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //minCapacity:10 elementData.length:0
grow(minCapacity); //10
}
第五步:调用grow()
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //0
int newCapacity = oldCapacity + (oldCapacity >> 1); //0=0+0
if (newCapacity - minCapacity < 0) //0-10<0
newCapacity = minCapacity; //newCapacity为10
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); //此时创建了长度为10的数组
}
三、总结
ArrayList源码分析:
1.jdk 7
ArrayList list = new ArrayList();
- 底层创建了长度是10的Object[] 数组elementData。
list.add(1);
...
list.add(11); //elementData[0] = new Integer(1)
- list.add(11); --->如果此次的添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
2.jdk 8中ArrayList的变化
ArrayList list = new ArrayList();
- 底层Object[] elementData初始化为{},并没有创建长度为10的数组
list.add(1)
...
- 第一次调用add()时,底层才创建了长度为10的数组,并将数据1添加到elementData[0]。
- 后续的添加和扩容操作与jdk 7无异。
小结 |
---|
jdk 7当中的ArrayList的对象的创建类似单例的饿汉式,而jdk 8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创 |
建,节省内存。 |