前言
为什么要使用集合呢?这个问题想必对很多刚接触集合的人来讲,是个疑问。举个简单的例子,你申明一个整形数组,我现在要10个空间,int array[] = new int[10],很简单呀,再举个简单的例子,想申请一个整形数组,这个时候怎么解决?Java不像c++,可以灵活的malloc,如果加入malloc这种,那么Java不就和c++一样,存在着指针等问题的烦恼了,为了解决这种类似的现象,Java决定采用集合的面向对象的思想。
add方法:
要想使用ArrayList,那么第一步肯定就是得知道如何添加元素进入数组。
第一种:在末尾添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
在分析这个之前,还记得上一篇博客中所说的默认初始容量为10么,继续分析:
首先,返回值是个boolean类型,add需要传入参数,当然是需要添加的元素,首先执行了ensureCapacityInternal(size + 1)。
分析ensureCapacityInternal(int minCapacity):
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
传入了一个整形的参数,在这个if判断这里,清楚的回顾之前的构造方法,相等的时候便是使用默认构造函数申明的数组(如果没有想明白,请自行在回顾构造方法)。如果是默认构造方法,将minCapacity赋值为默认值与传入参数两者较大的值,然后执行ensureExplicitCapacity(minCapacity)方法。
分析ensureExplicitCapacity(minCapacity):
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
传入参数为前面得到的minCapacity,然后操作数加一,因为存在多线程访问的可能性,这个操作数modCount就是这个作用,每次进行操作便进行记录。如果这个值大于当前的数组长度,执行grow(minCapacity);
分析:grow(minCapacity)
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
先记录原本的数组长度,newCapacity为原本长度加上原本长度的右移一位,,右移一位相当于除以2(这个是扩容的方法)。当newCapacity小于传入的长度,newCapacity就等于传入的这个,当这个newCapacity大于最大数组长度的时候,执行hugeCapacity(minCapacity)。
最大长度定义:
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
分析:hugeCapacity(minCapacity)
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
当入参小于0,抛出异常OutOfMemoryError,反之,判断如果大于最大长度,返回整型的最大长度,反之返回最大的长度。注意:这两个长度不同,一个是整型的最大长度,一个是自己定义的最大长度。
返回上一层,elementData = Arrays.copyOf(elementData, newCapacity);,这个有必要解释一下,这里仅仅是将空间进行扩大,并没有进行赋值。然后返回到最初的add方法了,中间部分基本已经全部解释清楚了。
最初的add方法的下一步为赋值,将新的元素赋值到末尾,一切都正确,返回true。
第二种,在指定位置添加元素
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
首先执行先判断这个index是否合法,是否超过最大的size。该方法过于简单,拒绝分析。接下来执行ensureCapacityInternal(size + 1); 分析过了,作用有两个,一扩容,第二增加modCount。然后执行本地方法拷贝,index之前的不变,index位置往后元素全部往后移动一位,赋值。
总结
总的来说,也是一般写方法的常规思想,首先判断空间够不够,如果空间不够如何扩容,扩容的方法为,本身加上本身除以二,记录对数组的操作,防止多线程出现多次操作导致访问不同。然后就是扩大的空间是否超过了最大的空间,最大的空间为最大的整型值减去8,然后就基本判断完了,该插入的插入即可。总的来说,还是较为简单的。
积土成山,风雨兴焉!!!