ArrayList源码分析
ArrayList源码分析
前言
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建以一个新的长度为10的数组
- 存满时,会扩容1.5倍数
- 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准,数组长度是存入元素还要+10
今天源码深度分析:
源码剖析第一二点
ArrayList<String> list=new ArrayList<>();
list.add("aaa");
首先调用
public boolean add(E e)
方法,进入源码
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
可以看到,
第一步,modCount=1
protected transient int modCount = 0;
第二步,调用了add的重载方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
然后重载方法中传递了三个参数,
第一个是“aaa”,理解为:要添加的元素
第二个是一个Object类型的数组elememData,可以看到源码发现他是一个空数组"",理解为:集合底层的数组的名字
第三个是size,集合大小size=0,理解为:集合的长度或者当前应存入元素的位置
transient Object[] elementData;
第二.1步,判断s和elementData的长度是否相等
明显是相等的,然后调用了grow()方法
private Object[] grow() {
return grow(size + 1);
}
第二.2步:返回值是一个object类型的数组,grow(1)
第三步调用grow()的有参构造,传的值是1,minCapacity=1
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
第三.1步
int oldCapacity = elementData.length;
用oldCapacity记录老容量,等于oldCapacity=0;
走入判断,明显都不满足,这个判断意思就是oldCapacity是不是大于零,并且做了一个非空判断,其实只需要看第一个不满足,整个判断为false,但是其实这两个都没满足,于是走了else语句
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
最后创建了一个对象使用Math.max获取最大值,源码中发现DEFAULT_CAPACITY=10,minCapacity等于传进来的1
private static final int DEFAULT_CAPACITY = 10;
在这里我们就发现了实际上就是创建了一个10的数组给了elementData并且做了一个返回
这里走完了我们就回到了add的重载方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
把elementData[0]="aaa";
size=1
直到这里add方法的第一次添加才算走完,这里就充分验证了
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建以一个新的长度为10的数组
没有添加数据的时候,默认长度是空数组,就是长度为0
添加第一个就会创建以一个新的长度为10的数组
扩容机制
- 接下来我们验证第三点,为什么存满时候会扩容1.5倍
这一次,我们假设现在正在添加第11个元素,假如第11个元素内容是"qqq",并且这里已经存满了10个元素
还是走进
第一步
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
这是后modCount=10
还是继续调用
第二步
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
第一个是“qqq”,理解为:要添加的元素
第二个是一个Object类型的数组elememData[10]
第三个是size,集合大小size=10,理解为:集合的长度或者当前应存入元素的位置
第二.1步
判断s和数组长度是否相等,明显是相等的
调用grow()
private Object[] grow() {
return grow(size + 1);
}
第二.2步
继续调用grow有参构造并且作返回
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
oldCapacity,老数组容量就是10,oldCapacity=10
第二.3步
接下来判断,明显第一个(老数组长度是否大于0)和第二个(非空判断)都满足,走进if语句
定义一个newCapacity,代表新容量
调用了ArraysSupport.newLength方法()传递了三个参数
第一个参数:oldCapacity,老数组的容量,返回值是10
第二个参数:minCapacity - oldCapacity,minCapacity=11减去oldCapacity=10,即返回值是1
第三个参数: oldCapacity >> 1 ,oldCapacity>>1,在二进制中进行右移,也就是把oldCapacity除于2,返回值是5
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
第三步
第三.1步
进入ArraysSupport.newLength()方法
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
这里就是prefLength=10+Math.max(1,5)这里的minGrowth, prefGrowth,就是传进来的第二个和第三个参数古
最后等于15
第三.2步
进入判断
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
@Native public static final int MAX_VALUE = 0x7fffffff;
也就是Integer最大值减8
直接返回了prefLength=15
回到了grow()方法
新容量就是
newCapacity=15
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
} else {
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
回到第7行
调用Arrays.copyOf()方法,传递的参数是(elementData[10],15)把原数组elementData10个数据拷贝到新elementData中,后面的数据补null,并且长度这里就已经是15了,并且把新数组作为一个返回
第四步
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
继续执行第四行代码
把索引10传递进去,然后把"qqq"添加为11个元素
最后size+1
第五步
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
回到add方法,把添加成功作为true进行返回
验证第四点
很简单,在上面验证第三点的时候我们翻到
第二.3步,假如我们传递的是第101个数据,里面已经又了100个元素
prefLength=10+Math.max(1,100),然后作为返回,数组长度就是110
这里就完全验证了第四点