1.什么是ArrayLsit
ArrayList是JDK中的一个基于数组实现的线性的可变长度的集合类,并且实现了List接口。
2.ArrayList构造方法分析
不指定容量时
//创建ArrayList
ArrayList list = new ArrayList();
创建完成后,点击进入ArrayList源码,可以发现是
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这样的一个无参构造方法对ArrayList
进行了初始化操作,其中elementData
是一个Object
类型的数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
也是一个Object
类型的数组且长度为0。
指定容量时
//创建ArrayList
ArrayList list = new ArrayList(20);
再次点击就会发现会调用一个制定了初始化容量的构造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
根据代码可知,我们传入参数后会创建一个容量为20
的数组。如果传入的不是20
而是0
的话,就会创建一个容量为空的数组,也就是this.elementData = {}
。
3.add()方法
//创建ArrayList
ArrayList list = new ArrayList();
list.add("aaa");
点击进入之后可以发现如下代码
public boolean add(E e) {
//用于检测容量是否够用
ensureCapacityInternal(size + 1); // Increments modCount!!
//将元素添加进集合中,并且维护size的值。 size的初始值为0
elementData[size++] = e;
return true;
}
点击ensureCapacityInternal(size + 1);
会发现如下代码
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
从代码中可以看出传入的是添加元素需要的最小空间,然后再点击calculateCapacity
方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY = 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
发现传入的是一个Object
类型的数组和需要的最小容量。因为elementData
是空的,所以会进入第一个if
判断,这样会从10和0
之间选取最大值,结果显而易见,所以10被返回到ensureExplicitCapacity
的参数列表中。
接着再点击ensureExplicitCapacity
。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//扩容函数
grow(minCapacity);
}
从这里可以看出会将刚才传入的10传到扩容的函数中,再点击grow
private void grow(int minCapacity) { //minCapacity = 10
//elementData是一个空数组,所以oldCapacity为0
int oldCapacity = elementData.length;
// newCapacity = 0 + 0 所以newCapacity = 0
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 0 - 10 < 0 true
if (newCapacity - minCapacity < 0)
// newCapacity = 10
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);
}
从中得出newCapacity = 10
,然后调用copyOf
方法进行复制,复制完成就会将elementData
的长度设置为10。
总结:在第一次调用add()方法时,会将容量初始化为10,但是如果总是执行添加操作,就会再次到达ArrayList的扩容标准
向list中添加元素到11次
//创建ArrayList
ArrayList list = new ArrayList();
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa"); //第11次调用add()方法
此时我们再执行上面的操作,直到这里
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 11 - 10 > 0 true
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
再次执行扩容操作
private void grow(int minCapacity) { //minCapacity = 11
// oldCapacity = 10
int oldCapacity = elementData.length;
//newCapacity = 10 + 10 / 2 15
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 15 - 11 < 0 false
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 进行copy
elementData = Arrays.copyOf(elementData, newCapacity);
}
当拷贝完成扩容操作执行完毕,elememtData
的容量此时为15
总结:当容量进行扩容时会将容量扩容为原来的1.5倍
4.get()方法
System.out.println(list.get(2));
get
方法需要传入数组下标,并通过下标返回数组中的值。点击get
方法会显示如下内容
public E get(int index) {
//检查下标合法性,不合法则抛出异常
rangeCheck(index);
//直接返回数组中下标为index的元素
return elementData(index);
}
5.set()方法
list.set(2, "bbb");
这里表示想将数组中下标为2的元素设置为bbb
。点击进入
public E set(int index, E element) {
//检查下标合法性
rangeCheck(index);
//保存索引为index位置的值
E oldValue = elementData(index);
//将传入的元素设置在index位置
elementData[index] = element;
//返回保存的元素
return oldValue;
}
通过传入index和E
类型的元素,更新完成后将原来的元素值返回。
6.remove()方法
System.out.println(list.remove(2));
删除下标为2的元素,并将删除前的元素返回,点击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;
}
7.iterator()方法
//创建ArrayList
ArrayList list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//创建迭代器
Iterator it = list.iterator();
//判断迭代器中是否还有下一个元素
while (it.hasNext()){
//将对象取出
Object o = it.next();
System.out.println(o);
}
迭代器可以遍历ArrayList
中的元素,点击iterator()
public Iterator<E> iterator() {
return new Itr();
}
在迭代器内部创建了一个Itr
对象,再点击
//指向元素的游标,当前cursor = 0
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
//迭代器中是否包含下一个元素
public boolean hasNext() {
return cursor != size;
}
//将游标向后移动
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
//将cursor向后移动
cursor = i + 1;
return (E) elementData[lastRet = i];
}
进入之后关注以上方法及参数
因为上面添加了3个元素,所以size
为3,cursor
与size
不相等,返回true
,所以会进入while
循环并执行next
方法,首先会将cursor
赋值给i,此时i为0,然后cursor向后移动最后将下标为0的元素返回。一直循环下去直至hasNext
返回false
。