java集合类源代码分析_ArrayList
???? 简介
???? 继承结构
目录结构 ???? 核心属性
???? 核心方法
-
-
-
-
-
- ???? 构造函数
- ???? 扩容方法
- ???? 新增元素
- ???? Arrays.copyOf 和 System.arraycopy
-
-
-
-
???? 总结
???? ArrayList简介
ArrayList就是动态数组,能够灵活的设置数组大小,提供了addALL,removeAll等数组中没有的方法,允许存储重复的元素和null。
???? 继承结构
-
继承了AbstractList,实现了List接口;
- 实现了RandomAccess接口,支持快速随机访问,实际上RandomAccess为空的接口,ArrayList内部维护了一个Object数组,数组本身就支持快速随机访问;
-
实现了Serializable接口,支持序列化;
- 实现了Cloneable接口,支持浅拷贝;
???? 核心属性
/** * 默认的数组大小 */ private static final int DEFAULT_CAPACITY = 10; /** * 调用指定大小或传入集合的构造函数,传入的大小为0或集合大小为0时,默认初始化一个空的Object数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 调用空的构造函数时,初始化一个默认的空的Object数组 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 内部维护的一个Object数组,transient表示不可序列化 */ transient Object[] elementData; // non-private to simplify nested class access /** * ArrayList中包含元素的个数 */ private int size;
???? 核心方法
- ???? 构造函数
/** * 指定大小的构造函数 * * @param initialCapacity 初始化时指定ArrayList的大小 * @throws IllegalArgumentException 如果指定大小小于0,抛出非法参数异常 */ public ArrayList(int initialCapacity) { //如果指定的大小大于0,则初始化一个该大小的Object数组 if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //如果指定的大小为0,则初始化一个空的Object数组 this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } /** * 空的构造函数,初始化一个空的Object数组 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } /** * 指定集合的构造函数 * * @param c 集合类对象 * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { //调用Collection对象的toArray方法返回该集合元素的Object数组 elementData = c.toArray(); //如果集合的大小不为0 if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) //如果Collection对象的toArray方法返回的不是Objcet类型的数组 if (elementData.getClass() != Object[].class) //通过Arrays.copyof方法转换成Object数组 elementData = Arrays.copyOf(elementData, size, Object[].class); } else { //如果集合的大小为0,则初始化空的Object数组 this.elementData = EMPTY_ELEMENTDATA; } }
- ???? 扩容方法
/** * * 如有必要,可以通过此方法增加ArrayList实例的容量,以确保它至少能够容纳元素的数量 * @param minCapacity 最小需要的容量 */ public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } /** * 得到扩容的最小需要的容量 */ private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } /** * 判断是否需要扩容 */ private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果容量已经超过数组的长度,则需要扩容 if (minCapacity - elementData.length > 0) //扩容操作的核心方法 grow(minCapacity); } /** * ArrayList的最大容量:2147483639 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /** * ArrayList扩容的核心方法 * @param minCapacity 最小需要的容量 */ private void grow(int minCapacity) { // 获取扩容前数组的长度 int oldCapacity = elementData.length; //计算扩容后的长度:旧容量大小 + 旧容量大小右移1位(即除2) = 1.5倍旧容量的大小 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果扩容后的容量依旧不够,则将最小需要的容量作为新的容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //判断新的容量是否超过ArrayList规定的最大容量 if (newCapacity - MAX_ARRAY_SIZE > 0) //1.如果最小需要的容量超过了最大容量,则新容量大小为Integer.MAX_VALUE //2.如果最小需要的容量没有超过最大容量,则新容量大小为MAX_ARRAY_SIZE newCapacity = hugeCapacity(minCapacity); // 调用Arrays.copyOf方法构建一个新的包含原有元素的大小为新容量大小的Object数组 elementData = Arrays.copyOf(elementData, newCapacity); } /** * 判断最小需要的容量和ArrayList最大容量的大小 */ private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
- ???? 新增元素
/** * 新增元素,返回true */ public boolean add(E e) { //调用扩容方法 ensureCapacityInternal(size + 1); // Increments modCount!! //将该元素放到下标为数组元素个数+1的位置 elementData[size++] = e; return true; } /** * 向数组指定位置添加元素时,检查指定位置的合法性 * 若指定位置的大小超过ArrayList中包含元素个数或小于0则抛出索引越界异常 */ private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } /** * 向ArrayList指定位置 * * @param index 指定存入ArrayList的位置 * @param element 新增的元素 * @throws IndexOutOfBoundsException {@inheritDoc} */ public void add(int index, E element) { //检查指定位置的合法性 rangeCheckForAdd(index); //调用扩容方法,此时最小需要的容量为元素个数+1 ensureCapacityInternal(size + 1); // Increments modCount!! //调用System.arraycopy进行数组的拷贝 System.arraycopy(elementData, index, elementData, index + 1, size - index); //将该元素存入数组的指定位置 elementData[index] = element; //ArrayList元素的个数加1 size++; } /** * 将集合实例中元素全部新增至ArrayList * * @param c collection 集合实例对象 * @return <tt>true</tt> if this list changed as a result of the call * @throws NullPointerException if the specified collection is null */ public boolean addAll(Collection<? extends E> c) { //通过Collection接口的toArray方法得到集合实例的Object数组 Object[] a = c.toArray(); //获取集合中元素的个数 int numNew = a.length; //调用扩容方法,此时最小需要的容量 = ArrayList已有的元素个数 + 新增元素的个数 ensureCapacityInternal(size + numNew); // Increments modCount //通过arraycopy的方法将集合实例中的元素拷贝至ArrayList中 System.arraycopy(a, 0, elementData, size, numNew); //此时ArrayList中元素的个数 = 原有个数 + 集合中元素个数 size += numNew; return numNew != 0; } /** * 将集合实例中元素全部新增至ArrayList的指定位置 * @param index 新增开始的位置 * @param c collection containing elements to be added to this list * @return <tt>true</tt> if this list changed as a result of the call * @throws IndexOutOfBoundsException {@inheritDoc} * @throws NullPointerException if the specified collection is null */ public boolean addAll(int index, Collection<? extends E> c) { //检查指定位置是否合理 rangeCheckForAdd(index); //通过Collection接口的toArray方法得到集合实例的Object数组 Object[] a = c.toArray(); //获取集合中元素的个数 int numNew = a.length; //调用扩容方法,此时最小需要的容量 = ArrayList已有的元素个数 + 新增元素的个数 ensureCapacityInternal(size + numNew); // Increments modCount //复制的元素个数 = 原有的元素个数 - 指定位置 //[1,2,3,4,5,6,0,0,0,0] addAll(1,[7,8]) //[1,0,0,2,3,4,5,6,0,0] 即数组元素从index位置开始整体向后移动numNew位 int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); //将集合数组的元素拷贝至ArrayList数组 //[1,7,8,2,3,4,5,6,0,0] System.arraycopy(a, 0, elementData, index, numNew); //ArrayList的元素个数 = 原有个数 + 集合中元素个数 size += numNew; return numNew != 0; }
- ???? Arrays.copyOf 和 System.arraycopy
在ArrayList源代码的核心方法中多次使用了Arrays.copyof和System.arraycopy,这两个方法有什么联系和区别?
/** * Copies the specified array, truncating or padding with nulls (if necessary) * so the copy has the specified length. For all indices that are * valid in both the original array and the copy, the two arrays will * contain identical values. For any indices that are valid in the * copy but not the original, the copy will contain <tt>null</tt>. * Such indices will exist if and only if the specified length * is greater than that of the original array. * The resulting array is of the class <tt>newType</tt>. * * @param <U> the class of the objects in the original array * @param <T> the class of the objects in the returned array * @param original the array to be copied * @param newLength the length of the copy to be returned * @param newType the class of the copy to be returned * @return a copy of the original array, truncated or padded with nulls * to obtain the specified length * @throws NegativeArraySizeException if <tt>newLength</tt> is negative * @throws NullPointerException if <tt>original</tt> is null * @throws ArrayStoreException if an element copied from * <tt>original</tt> is not of a runtime type that can be stored in * an array of class <tt>newType</tt> * @since 1.6 */ 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将目标数组的元素拷贝到新建的数组中去 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); //返回新的数组对象 return copy; }
通过分析Arrays.copyof的源码可知,Arrays.copyof内部也是通过System.arraycopy实现数组的拷贝。
/** * * @param src 源数组 * @param srcPos 源数组的起始位置 * @param dest 目标数组 * @param destPos 目标数组的起始位置 * @param length 拷贝的长度 */ public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
通过分析两者的源代码可得出:
1. Arrays.copyof 是自己在内部新建一个数组,将源数组的内容拷贝到新建的数组中去,并返回新的数组对象;
2. System.arraycopy需要源数组和目标数组,将源数组的内容拷贝到自定义的数组中;
???? 总结
- ArrayList实现了List接口,为有序集合,允许存储重复的元素和null;
- ArrayList内部维护一个动态数组,支持快速随机访问;
- ArrayList每次调用add方法新增元素时,都会调用扩容方法判断是否需要扩容,如果需要则扩容为原来容量的1.5倍;
- 因为ArrayList的扩容机制需要耗时,因此在存储大量元素时,最好指定最小需要的容量;
- ArrayList不是线程安全的,当多个线程对其进行结构上的操作时,程序会抛出ConcurrentModificationException异常,原因在于fast-fail机制,即迭代器在遍历元素时,会使用一个modCount变量,每当迭代器使用hasNext或next前,都会检查modCount的值是否等于期望的值。