ArrayList详解-源码分析 (2)

Arrays类的copyOf()方法源代码如下:

@SuppressWarnings("unchecked") public static <T> T[] copyOf(T[] original, int newLength) { // 此处的copyOf()调用了本类的重载方法 return (T[]) copyOf(original, newLength, original.getClass()); }

重载方法源代码如下所示:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }

代码分析:

使用三元运算符进行判断传入数组的类型

如果传入的数组类型强转为Object[]数组为true,则创建一个Object[newLength]数组赋值给copy

如果为false,则利用反射获取到传入数组的类型,创建一个该类型的指定长度的数组赋值给copy

调用native方法进行赋值

最后返回copy

调用链最后调用到了native方法上:

public static native void arraycopy(Object src, int srcPos,Object dest, int destPos, int length); /** * 参数说明: * src:源对象 * srcPos:源数组中的起始位置 * dest:目标数组对象 * destPos:目标数据中的起始位置 * length:要拷贝的数组元素的数量 */

源代码中最后都调用到了native方法,只能看到方法名和参数,看不到具体的实现,对native方法做个简单的介绍吧。

native声明的接口方法: Java代码和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。也就是说,带有native标记的方法,都是使用C语言来实现的,读者了解到这里即可,感兴趣的可以去查阅下相关资料,这里就不多阐述了。

5. 常用方法分析

add(E e)

源代码如下所示:

public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }

初步分析:

调用一个ensureCapacityInternal()方法,ensure Capacity Internal 直接谷歌翻译,意思是确保内部容量,此方法内应该就隐藏着ArrayList动态扩容的方法了!

将传入参数e,赋值给elementData[]数组中下标为size++的元素

返回true

继续跟踪ensureCapacityInternal()方法,该方法相关源代码如下所示:

private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); }

分析:

先对elementData数组进行空数组判断,注意:这里是直接使用 ==运算符来进行判断的,回顾下前面讲到的ArrayList的无参构造方法

public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }

发现了没?如果使用默认的构造方法,调用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,执行add()方法之前,ArrayList数组的长度都是零,添加第一个元素后,数组的长度就变为10了。

下一个方法是ensureExplicitCapacity(),我们继续跟踪。

该方法源代码如下所示:

private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }

初步分析:

出现了一个前面没看到的变量modCount

里面还有一个grow()方法,ArrayList能够动态扩容的原因就在这个方法里面了,八九不离十了!grow这个单词我还是认识的,哈哈哈~~~

继续分析:

经过追踪,发现modCount是ArrayList 的父类AbstractList的一个成员变量,作用是记录ArrayList的size变化,添加元素时,该变量会自增一次。

minCapacity变量是要添加元素在elementData数组里的索引,当该变量值超过elementData数组长度的时候,elementData数组就要进行动态扩容了!

接下来,看下grow()方法的源代码,马上就要揭开ArrayList能够动态扩容的根本原因了,想想还有些激动,哈哈~

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); }

分析:

将当前未添加新元素的elementData的数组长度赋值给oldCapacity变量,表示旧数组的容量

定义一个变量newCapacity,表示新数组的容量,新数组的容量大小为旧数组容量的1.5倍

此处用到了移位操作,>>是移位运算符,表示带符号数右移

向右移动n位,等同于除以2的n次方

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

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