深入浅出!阿里P7架构师带你分析ArrayList集合源码,建议是先收藏再看! (2)

ensureExplicitCapacity 方法中首先对modCount 自增1,记录操作次数,然后如果 minCapacity 大于 elementData 的长度,则对集合进行扩容。显然当第一次添加元素时 elementData 的长度为零。那我们来看看 grow 函数。

private void grow(int minCapacity) { // ArrayList的旧容量为数组长度 int oldCapacity = elementData.length; // 将新容量赋值为原容量的1.5倍(左移一位相当于除以二) int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果此时新容量还是小于添加元素后的容量,则将新容量直接赋值为添加元素后的容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果此时新容量大于数组的最大大小,则返回上限最大的容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 把旧的数组elementData拷贝到新的elementData,并将容量设置为newCapacity elementData = Arrays.copyOf(elementData, newCapacity); }

默认将list的容量扩容至原来容量的 1.5 倍。但是扩容之后也不一定适用,有可能太小,有可能太大。所以才会有下面两个 if 判断。如果1.5倍太小的话,则将增加元素的容量大小赋值给newCapacity如果1.5倍太大或者我们需要的容量太大,则调用hugeCapacity函数,给newCapacity赋一个合适的值。最后将原数组中的数据复制到大小为 newCapacity 的新数组中,并将新数组赋值给 elementData

private static int hugeCapacity(int minCapacity) { // 如果minCapacity小于0,就抛出异常 if (minCapacity < 0) // overflow throw new OutOfMemoryError(); // 如果此时增加元素后得minCapacity大于数组的最大长度就返回整数最大值,否则返回数组最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; } add方法(批量添加,在指定位置添加) public void add(int index, E element) { // 检查index是否越界 rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }

这三个方法基本思路与上述add方法基本思路是一致,博主这里就不再赘述了。

remove方法 public E remove(int index) { // 检查index是否越界,如果越界则抛出异常 rangeCheck(index); // modCount自增,修改次数加一 modCount++; // 获取elementData在index位置的值 E oldValue = elementData(index); // 获取后移的位置长度 int numMoved = size - index - 1; // 如果大于零,则调用System.arraycopy方法完成数组移动 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); // size自减,并将elementData索引值为size的元素引用赋值为空,让GC对他进行回收 elementData[--size] = null; // 返回index位置的值 return oldValue; }

当我们调用 remove(int index) 时,首先会检查 index 是否合法,然后再判断要删除的元素是否位于数组的最后一个位置。如果 index 不是最后一个,就再次调用 System.arraycopy() 方法拷贝数组。说白了就是将从 index + 1 开始向后所有的元素都向前挪一个位置。然后将数组的最后一个位置空,size - 1。如果 index 是最后一个元素那么就直接将数组的最后一个位置空,size - 1即可。

public boolean remove(Object o) { // 如果o为空,则查找数组中为空的索引,并调用fastRemove方法进行删除,并返回true if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } // 如果o不为空,则查找数组中与该元素相等的索引,并调用fastRemove方法进行删除,并返回true else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } // 如果list中不存在则返回false return false; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpysdz.html