怒开新坑,有一说一,我这菜鸟基础都还没整明白就弄多线程,这也太高估自己了~诶 计算机真卷
最近在看关于集合的源码,有幸能提出自己的见解
//Constructs an empty list with an initial capacity of ten.
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
这个是构造函数,也就是说,调用无参构造函数的时候,会构造一个长度为10的数组。
查看add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
通过这个方法,很容易发现,ArrayList内部的数据结构其实就是数组elementData[size++] = e;
并且,如果我们要在里面添加袁术的时候就会先判断我把这个对象添加之后这个对象的容纳能力,其实就是为了在下一次添加的时候缓存数组有足够的空间添加元素,之后把它放到数组末尾即可
查看ensureCapacityInternal()方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
如果目前的如果当前elementData为空的话,minCapacity=DEFAULT_CAPACITY,同时DEFAULT_CAPACITY的默认值是10,从这我们可以看出,在第一次初始化的时候,ArrayList内部会默认创建一个内部长度为10的数组。
ensureExplicitCapacity()方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//判断添加元素后,缓存数组时候需要扩展
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
该方法会记录当前数组的更改次数,并且判断当前数组添加后,是否需要进行增长,
grow方法
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);
}
该方法会扩展缓存为当前数组的长度为 原数组长度+原数组长度的二分之一,也就是按照原数组的50%进行增长,同时该数组最大的扩展长度是Integer.MAX_VALUE - 8。也就是ArrayList最多能存储的数据长度,通过扩展数组长度以后,在下一次添加数据的时候,ArrayList就有足够的空间去添加新的元素了。
get()获取元素
public E get(int index) {
if (index >= size)//判断当前角标长度是否超过数组长度,如果是抛出异常,反之返回数据
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
很容易看出来,就不细说了
remove(int)
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 将数组最后一位置null
return oldValue;
}
这里先判断删除角标是否超过数组长度,然后通过System.arrayCopty()方法将角标对应的元素删除。
这里对System.arrayCopty()方法解释一下。该方法的第一个参数是源数组,第二个参数是复制的开始角标,第二个参数是目标数组。第三个参数是目标数组与源数组的复制数据开始角标。最后一个参数是复制的长度。(注意:!!!复制的长度不能大于目标数组减去开始角标的长度或源数组减去开始角标的长度)
int[] a = {0, 1, 2, 3, 4};
int[] b = {5, 6, 7, 8, 9};
System.arraycopy(a, 0, b, 1, 3);
// 则进行操作后 b = {5,0,1,2,9}
remove(Object)
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
}
和上面一致,只是换成对象再判断下标而已