ArrayList源码分析
我这个是jdk1.7的版本,但是我看了下跟1.8有点相似,可能有点不一样。
首先我们先来看看这个的继承和实现关系。
这个类继承了AbstractList,并且实现了集合重要的接口List。
AbstractList类对List集合中的方法进行了一些重写。
我们直接从ArrayList的一些方法直接入手,有需要了解的地方在回来看看前面的东西。
- list.add(E e);
我们可以看到有一个ensureCapacityInternal(size+1)方法。
接下来我们去到这个方法。
然后我们来看看,这里面的一些参数。
到这里我们先去看看ArrayList的构造方法。
第三个构造方法,这里做了要求,必须是
private static final Object[] EMPTY_ELEMENTDATA = {}创建一个空的数组,用于空的实例,有点绕。
private transient Object[] elementData
源码的解释是:数组缓冲区中的ArrayList的元素存储。ArrayList的容量是这个数组缓冲区的长度。任何空列表elementdata = = empty_elementdata将扩大default_capacity当第一元素添加。
个人理解:相当于第一次添加元素的时候,这两个数组肯定都是空的,然后通过Math.max()函数,取出两个中最大的值,用于确定明确的容量。
然后调用ensureExplicitCapacity(minCapacity);方法。我看了一下1.8的源码也有这个,这个作用应该是防止,第一次有人故意创建一个为空的数组。
ArrayList list=new ArrayList(0);这种情况。
在去到这个方法。
modCount++用于快速失败用的一个参数。主要是防止多线程访问的时候,有的线程删除,添加数据,出错。
minCapacity - elementData.length > 0用于判断是否需要扩容。如果需要就走grow()方法。
去这个方法看看。
int oldCapacity = elementData.length;//获取旧数组的长度。
int newCapacity = oldCapacity + (oldCapacity >> 1);
扩容取到一个新数组的长度。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
如果新创建的数组长度任然小于需要的长度。那就直接赋值,把传过来的长度赋值给新数组,这种情况应该会出现在,添加集合的时候,添加的元素很多。一般不会出现。
if (newCapacity - MAX_ARRAY_SIZE > 0)
首先我们需要看看这个参数的含义。
我们可以看到一个关键的单词OutOfMemoryError,这个就是防止动态扩展的时候,出现内存溢出。如果太大了,我们就要调用下面的方法做出处理。一句话,这个参数的作用就是防止溢出的最大限制。最大的扩容大小。相当于一个优化,以前的版本没有。
newCapacity = hugeCapacity(minCapacity);
所以我们在进入hugeCapacity()方法。
如果minCapacity大于这个防止内存溢出的参数了。返回Integer的最大值,否则返回溢出限制的参数。
然后来到这个方法。
elementData = Arrays.copyOf(elementData, newCapacity);
复制这个elementData数组,把他的长度扩容到newCapacity。
到了这里,我们的add()方法里面的第一行才解决了。累懵逼了。
我们接着看里面的第二个方法。
elementData[size++] = e;
在数组中插入元素e,然后在size++。
到现在为止我们的add()方法就已经讲完。我写的有问题的地方大家留言。有的地方我也没怎么明白。一起学习。
- add(int index, E element)
现在我们来分析分析,在想要的位置插入。
首先先看源码的解释:在指定的位置的list中插入元素,移动当前处于该位置的元素(如果有的话)和右边的任何后续元素(增加一个索引)。
可能的异常:数组下表越界。
我们首先看:rangeCheckForAdd(index)方法。
一个非常简单的方法。就是判断插入的位置,是否合理。
ensureCapacityInternal(size + 1);然后我们又看到了这个方法。保证能够顺利的插入。不解释了。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
调用这个函数,把index的数和以后的数往后移动。
elementData[index] = element;
size++;
然后在index位置插入。
最后数组长度+1
讲一个批量删除的,其余的删除就不讲了
3.private boolean batchRemove(Collection<?> c, boolean complement)
我们从这个for循环开始。但是我们发现有一个contains()方法不知道。去看看这个方法。
再去到这个indexOf()
很简单就是一个比价,取到元素的下标值,返回。
然后回到contains()方法,作比较,下标大于0,证明比较的元素在集合里面存在。返回true。
再到删除的方法中。
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
如果存在,就把这个数,存在这个集合的第一位。用的同一个集合,这个处理很巧妙,不用new一个新的数组,这样节约内存了。等会我们就直接根据w r的值,我们直接把不要的元素直接扔掉就行。
上面图中标 红色的圈,这段没搞明白,我觉得他应该不会执行的啊。个人认为可能是多线程的时候会出现的问题。
然后到下面一个if进行了元素的置空。注意是置空,不是删除的长度,因为这个空间我们已经申请好了,没有必要清除了,以后也可以用。
ArrayList源码就到这里,下次我们撸hashmap的源码。