一:创建一个ArrayList对象,并添加第一个元素,看看发生了什么?
package Test01;
import java.util.ArrayList;
public class TestArrayList {
public static void main(String[] args) {
/**
* 新建+添加干了什么?
*/
ArrayList arraylist = new ArrayList();
//放第1个
arraylist.add("aaa");
}
}
Ctrl+鼠标点击ArrayList进入源码查看,第1句,初始化了一个空的Object类型的数组,size=0;
1、private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2、transient Object[] elementData;
3、private int size;
4、private static final int DEFAULT_CAPACITY = 10;
第1步:空构造器初始化,底层数组elementData初始化为空
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
第2步:现在添加一个元素e
//添加一个元素时发生了什么?
public boolean add(E e) { //添加“aaa”
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e; //下标为0索引添加元素e
return true;
}
第3步:首先查看ensureCapacityInternal方法
因为size=0,所以size+1=1;
这时传入一个参数minCapacity = 1
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
解释:如果这个底层数组为原始创建的那个,那么进入if语句里,把minCapacity长度设置为10,因为第一次肯定是空的,所以此时的minCapacity =10;
第4步:然后进入下面语句
//明确容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
if语句里,10-0>0成立,ok,进入grow方法进行创建,minCapacity依旧是10
第5步:扩容与复制
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);
}
我们会进入第5步的第一个if语句里,将newCapacity = 10;然后复制一个新数组,长度为10
至此数组扩容完事,然后回到第2步,将元素添加至数组索引为0处。
到这里,就讲解了添加一个元素的实现过程。
二:当添加多个元素时候,特别是元素数据超过size大小10时,内部实现过程?
第1种情况:数据量小于等于10:
当在添加一个的基础上再添加一个元素时,首先第3步中,不会进入if语句了,会直接调用ensureExplicitCapacity(2);
然后第4步中 if (minCapacity - elementData.length > 0)已经不成立了,因为上一步已经把数组扩容至10,所以数据量elementData.length 在10个以内的都不会调用grow方法进行扩容。
第2种情况,数据量大于10
package Test01;
import java.util.ArrayList;
public class TestArrayList {
public static void main(String[] args) {
ArrayList arraylist = new ArrayList();
//放了10个
arraylist.add("aaa");
System.out.println(System.identityHashCode(arraylist));
arraylist.add("bbb");
arraylist.add("ccc");
arraylist.add("ddd");
arraylist.add("eee");
arraylist.add("fff");
arraylist.add("ggg");
arraylist.add("hhh");
arraylist.add("iii");
arraylist.add("jjj");
//放第11个
arraylist.add("ahdioa");
System.out.println(System.identityHashCode(arraylist));
}
}
//356573597
//356573597
我们从第5步开始,数据量是第11个了,也就是minCapacity = 11;
oldCapacity = 10,进入第4步的if语句,开始扩容,扩容多少呢?
是1.5倍,newCapacity =10+10>>1=15。
如此循环往复,我们还可以注意到,每次扩容是用拷贝的方法,在原数组的内存地址中往后面开辟新的空间。
函数原型:copyOf(oringinal, int newlength)
oringinal:原数组 newlength:复制数组的长度
这个方法是从原数组的起始位置开始复制,复制的长度是newlength
三:一些方法的源码了解
remove
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
常考面试题:
如果我们用for循环去实现ArrayList的元素删除,能实现吗?
答:是要看实际情况的
①如果for循环从最小索引到最大索引,那么是无法实现的,因为它的底层是数组,数组删掉前面的元素,后面的是会移上去的,所以去删第二个索引的时候,第一个就会还留着,依次类推。
②如果for循环从最大索引到最小索引,那么是可以的,因为没有了数据的移动造成的问题。
我们传入一个值,会进入else中,先是用一个遍历去查找索引,然后把索引当做参数传入fastRemove中
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
这一步主要做的是将从索引位置开始的后面元素一起拷贝到索引位置处,覆盖索引处的数据,实现数据的删除。
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
clear ——遍历清空
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
set——置换元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
get——获取元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
好了,ArrayList就先学习到这里,后期会不断更新哦!