Java ArrayList源码解析(基于JDK 12,对比JDK 8)

自从接触到ArrayList以来,一直觉得很方便,但是从来没有系统、全面的学习了解过ArraryList的实现原理。最近学习了一下ArrayList的源码学习一下,顺便记录一下,防止后面遗忘太快。现在的jdk版本更新比以前快了很多,截止发文已经更新到了JDK 15了,本文查看的源码是JDK 12的,native 方法阅读的是jdk 8的,也有对比jdk 12 与jdk 8的部分源码.
重要的事情说三遍,
本文查看的源码的jdk 12,部分源码对比jdk 8!!!
本文查看的源码的jdk 12,部分源码对比jdk 8!!!
本文查看的源码的jdk 12,部分源码对比jdk 8!!!

阅读前须知:
源码中使用的 System.arraycopy(elementData, index, elementData, index + 1, s - index)与 Arrays.copyOf(elementData, size, Object[].class)这两个方法是属于浅拷贝,关于浅拷贝以及System.arraycopy方法的底层C/C++方法可以看这两篇文章:
1、浅拷贝与深拷贝:https://www.cnblogs.com/shakinghead/p/7651502.html
2、System.arraycopy方法的底层C/C++方法:https://blog.csdn.net/fu_zhongyuan/article/details/88663818
我在网上找了一下该方法浅拷贝的指向原理

一、 ArrayList初始化

ArrayList有三种初始化方法:
1、初始化一种指定集合大小的空ArrayList;
2、初始化一个空的ArrayList;
3、初始化一个含有指定元素的ArrayLsit。
具体源码与注释一起放在下面了

 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};private static final Object[] EMPTY_ELEMENTDATA = {};transient Object[] elementData; // non-private to simplify nested class access/*** Constructs an empty list with the specified initial capacity.** @param  initialCapacity  the initial capacity of the list* @throws IllegalArgumentException if the specified initial capacity*         is negative*/public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}/*** Constructs an empty list with an initial capacity of ten.*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/*** Constructs a list containing the elements of the specified* collection, in the order they are returned by the collection's* iterator.** @param c the collection whose elements are to be placed into this list* @throws NullPointerException if the specified collection is null*/public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {// defend against c.toArray (incorrectly) not returning Object[]// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {// replace with empty array.this.elementData = EMPTY_ELEMENTDATA;}}

然后自己写了一个测试demo,试了一下这三个初始化方法,发现挺好用的

ArrayList常用方法

常用的方法无非就是增删改查

get( )与set()

改查这两个方法就很简单了,简单的先放在前面说

/*** Returns the element at the specified position in this list.** @param  index index of the element to return* @return the element at the specified position in this list* @throws IndexOutOfBoundsException {@inheritDoc}*/public E get(int index) {rangeCheck(index);return elementData(index);}/*** Replaces the element at the specified position in this list with* the specified element.** @param index index of the element to replace* @param element element to be stored at the specified position* @return the element previously at the specified position* @throws IndexOutOfBoundsException {@inheritDoc}*/public E set(int index, E element) {rangeCheck(index);E oldValue = elementData(index);elementData[index] = element;return oldValue;}

ArrayList新增

从下面源码可以看出,我们平时喜欢的使用的add(E e)其实调用的是上面的实现方法add(E e, Object[] elementData, int s),这里需要提的一点事add(int index, E element)这种指定元素、数组下标的新增方法。在这个方法里面使用了两个方法:grow()方法和arraycopy(Object src, int srcPos, Object dest, int destPos, int length),前者将数组容量扩大了一位,后者调用本地的native方法。
相关源码附在下面了:

/*** This helper method split out from add(E) to keep method* bytecode size under 35 (the -XX:MaxInlineSize default value),* which helps when add(E) is called in a C1-compiled loop.*/private void add(E e, Object[] elementData, int s) {if (s == elementData.length)elementData = grow();elementData[s] = e;size = s + 1;}/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/public boolean add(E e) {modCount++;add(e, elementData, size);return true;}/*** Inserts the specified element at the specified position in this* list. Shifts the element currently at that position (if any) and* any subsequent elements to the right (adds one to their indices).** @param index index at which the specified element is to be inserted* @param element element to be inserted* @throws IndexOutOfBoundsException {@inheritDoc}*/public void add(int index, E element) {rangeCheckForAdd(index);modCount++;final int s;Object[] elementData;if ((s = size) == (elementData = this.elementData).length)elementData = grow();System.arraycopy(elementData, index,elementData, index + 1,s - index);elementData[index] = element;size = s + 1;}/*** Increases the capacity to ensure that it can hold at least the* number of elements specified by the minimum capacity argument.** @param minCapacity the desired minimum capacity* @throws OutOfMemoryError if minCapacity is less than zero*/private Object[] grow(int minCapacity) {return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));}private Object[] grow() {return grow(size + 1);}@HotSpotIntrinsicCandidatepublic static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);

addAll(int index, Collection<? extends E> c)与 addAll(Collection<? extends E> c) 方法

这两个方法都是可以直接添加集合的方法,不同的是一个是尾部添加,一个是从索引处开始添加,源码贴在下面了。对比了JDK12 与JDK 8发现这两个方法没有做修改。

 /*** Appends all of the elements in the specified collection to the end of* this list, in the order that they are returned by the* specified collection's Iterator.  The behavior of this operation is* undefined if the specified collection is modified while the operation* is in progress.  (This implies that the behavior of this call is* undefined if the specified collection is this list, and this* list is nonempty.)** @param c collection containing elements to be added to this list* @return {@code true} 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) {Object[] a = c.toArray();modCount++;int numNew = a.length;if (numNew == 0)return false;Object[] elementData;final int s;if (numNew > (elementData = this.elementData).length - (s = size))elementData = grow(s + numNew);System.arraycopy(a, 0, elementData, s, numNew);size = s + numNew;return true;}/*** Inserts all of the elements in the specified collection into this* list, starting at the specified position.  Shifts the element* currently at that position (if any) and any subsequent elements to* the right (increases their indices).  The new elements will appear* in the list in the order that they are returned by the* specified collection's iterator.** @param index index at which to insert the first element from the*              specified collection* @param c collection containing elements to be added to this list* @return {@code true} 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);Object[] a = c.toArray();modCount++;int numNew = a.length;if (numNew == 0)return false;Object[] elementData;final int s;if (numNew > (elementData = this.elementData).length - (s = size))elementData = grow(s + numNew);int numMoved = s - index;if (numMoved > 0)System.arraycopy(elementData, index,elementData, index + numNew,numMoved);System.arraycopy(a, 0, elementData, index, numNew);size = s + numNew;return true;}

ArrayList删除

remove(int index)

先看jdk 12里面的remove(int index)删除方法,这是根据索引去移除元素,大家可以看下官方注释,在jdk 12里面对jdk 8里面的一些代码改用了封装方法,在这里进行数据越界校验之后,直接调用了fastRemove(es, index)方法,在fastRemove(es, index)里面我们可以看到,也是直接调用了本地的
System.arraycopy(es, i + 1, es, i, newSize - i);方法去实现的,再次提醒,这个方法出现的频次很高,如果大家对这个比较感兴趣的可以去看下这篇文章:https://blog.csdn.net/fu_zhongyuan/article/details/88663818

/*** Removes the element at the specified position in this list.* Shifts any subsequent elements to the left (subtracts one from their* indices).** @param index the index of the element to be removed* @return the element that was removed from the list* @throws IndexOutOfBoundsException {@inheritDoc}*/public E remove(int index) {Objects.checkIndex(index, size);//校验数组是否越界final Object[] es = elementData;@SuppressWarnings("unchecked") E oldValue = (E) es[index];fastRemove(es, index);return oldValue;}/*** Private remove method that skips bounds checking and does not* return the value removed.*/private void fastRemove(Object[] es, int i) {modCount++;final int newSize;if ((newSize = size - 1) > i)System.arraycopy(es, i + 1, es, i, newSize - i);es[size = newSize] = null;}

这里我们对比一下jdk 8 和jdk 12的remove( )方法,发现jdk 12除了将jdk 8的代码改用了封装方法之后没有其他较大的差异。

/*** Removes the element at the specified position in this list.* Shifts any subsequent elements to the left (subtracts one from their* indices).** @param index the index of the element to be removed* @return the element that was removed from the list* @throws IndexOutOfBoundsException {@inheritDoc}*/public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;}

对比完了之后,写了几行代码,我们看下这个的运行过程。

import java.util.ArrayList;public class ArrayListDemo {public static void main(String[] args) {ArrayList a = new ArrayList();a.add("1");a.add("2");a.add("3");a.remove(1);}}

在调用remove()的地方打上断点,我们首先看下进入断点的时候ArrayList是什么样子的

然后进入断点,我们看下在ArrayList源码里面是怎么执行的

执行到FastRemove( )方法里面,我们再来看下进行了什么操作呢?

最后可以看到数据被移除了

remove(Object o)

这个删除方法,我们可以看到jdk 12 对比jdk 8 没有很大的变化,该方法用途是删除ArrayList中第一个需要删除元素的值。这个比较简单大家看下面源码就理解了。

/*** Removes the first occurrence of the specified element from this list,* if it is present.  If the list does not contain the element, it is* unchanged.  More formally, removes the element with the lowest index* {@code i} such that* {@code Objects.equals(o, get(i))}* (if such an element exists).  Returns {@code true} if this list* contained the specified element (or equivalently, if this list* changed as a result of the call).** @param o element to be removed from this list, if present* @return {@code true} if this list contained the specified element*/public boolean remove(Object o) { //jdk12final Object[] es = elementData;final int size = this.size;int i = 0;found: {if (o == null) {for (; i < size; i++)if (es[i] == null)break found;} else {for (; i < size; i++)if (o.equals(es[i]))break found;}return false;}fastRemove(es, i);return true;}
/*** Removes the first occurrence of the specified element from this list,* if it is present.  If the list does not contain the element, it is* unchanged.  More formally, removes the element with the lowest index* <tt>i</tt> such that* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>* (if such an element exists).  Returns <tt>true</tt> if this list* contained the specified element (or equivalently, if this list* changed as a result of the call).** @param o element to be removed from this list, if present* @return <tt>true</tt> if this list contained the specified element*/public boolean remove(Object o) {//jdk 8if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}

按照惯例,我们还是用demo打印一下输出结果:



注:图一到图三分别是移除前,移除中,移除后。
划重点,此方法只能移除第一个索引的元素,那如果我们想移除ArrayList里面指定的全部元素怎么办呢?

removeAll(Collection<?> c) 与retainAll(Collection<?> c)

jdk12里面提供了removeAll(Collection<?> c) 方法去移除ArrayList里面指定的全部元素,这里需要注意的是,该方法的传参是Collection<?> c,这是什么意思呢,就是你需要传一个集合进去,不能直接传你想删除的单个元素。这里把retainAll(Collection<?> c) 一起讲,为什么呢?
大家看源码就可以发现,这两个方法里面都是调用了共同的方法 batchRemove(Collection<?> c, boolean complement,final int from, final int end)。我们一起看下这个方法。
该方法在第一个循环里面去判断Arraylist里面有无你想要删除或者保留的元素,为什么要执行这一步呢?
这样做的好处就是如果没有就不用进行后面的操作,不用去创建其他的对象,也不用去分配空间。
当ArrayList里面有这个元素再对这个元素进行删除和保留操作。

接下来我们看下是怎么实现保留和删除的,在第二个for循环里面,回去校验删除集合里面是否有包含目标对象元素,如果有,判断complement是保留还是删除,这里的设计真的很巧妙了。
complement为false的情况就是为删除,我们看下删除是怎么实现的呢?
在第一个循环里面我们拿到了目标集合(假设为A)存在删除集合(假设为B)里面元素的第一个索引(角标)。
在看源码,定义的变量w记住了这索引,然后开始进行第二个循环。
第二个循环的起点索引就是w+1,直接从第一个循环找到的索引开始遍历检查目标集合(A)是否存在删除集合(B)里面元素,如果不包含的,将目标集合(A)中w索引处的元素替换成的值当前,并且将w+1,否则就继续循环。对目标集合遍历完成之后,将对生成的集合调用shiftTailOverGap(es, w, end)方法去将多余的元素置空方便GC回收。

反之,complement为true的情况就是retainAll(Collection<?> c)方法,刚好反过来。

这一段文字凭空理解起来可能有点混乱,在下面我写了一个demo方便理解。

/*** Removes from this list all of its elements that are contained in the* specified collection.** @param c collection containing elements to be removed from this list* @return {@code true} if this list changed as a result of the call* @throws ClassCastException if the class of an element of this list*         is incompatible with the specified collection* (<a href="Collection.html#optional-restrictions">optional</a>)* @throws NullPointerException if this list contains a null element and the*         specified collection does not permit null elements* (<a href="Collection.html#optional-restrictions">optional</a>),*         or if the specified collection is null* @see Collection#contains(Object)*/public boolean removeAll(Collection<?> c) {return batchRemove(c, false, 0, size);}/*** Retains only the elements in this list that are contained in the* specified collection.  In other words, removes from this list all* of its elements that are not contained in the specified collection.** @param c collection containing elements to be retained in this list* @return {@code true} if this list changed as a result of the call* @throws ClassCastException if the class of an element of this list*         is incompatible with the specified collection* (<a href="Collection.html#optional-restrictions">optional</a>)* @throws NullPointerException if this list contains a null element and the*         specified collection does not permit null elements* (<a href="Collection.html#optional-restrictions">optional</a>),*         or if the specified collection is null* @see Collection#contains(Object)*/public boolean retainAll(Collection<?> c) {return batchRemove(c, true, 0, size);}boolean batchRemove(Collection<?> c, boolean complement,final int from, final int end) {Objects.requireNonNull(c);final Object[] es = elementData;int r;// Optimize for initial run of survivorsfor (r = from;; r++) {if (r == end)return false;if (c.contains(es[r]) != complement)break;}int w = r++;try {for (Object e; r < end; r++)if (c.contains(e = es[r]) == complement)es[w++] = e;} catch (Throwable ex) {// Preserve behavioral compatibility with AbstractCollection,// even if c.contains() throws.System.arraycopy(es, r, es, w, end - r);w += end - r;throw ex;} finally {modCount += end - w;shiftTailOverGap(es, w, end);}return true;}

demo代码放在下方了,并且将执行过程绘制了一下,大家可以自己去跑一跑深入理解一下这一过程。

import java.util.ArrayList;public class ArrayListDemo {@SuppressWarnings({ "unchecked", "rawtypes" })public static void main(String[] args) {ArrayList a = new ArrayList();ArrayList b = new ArrayList();a.add("1");a.add("2");a.add("3");a.add("4");a.add("2");a.add("5");b.add("2");//a.remove(1);//a.remove("2");a.removeAll(b);//a.retainAll(b);System.out.print("1");}}


其他实用方法

ArrayList.trimToSize()方法

该方法会将ArrayList的容量调整为当前大小,减少数据空间。这个方法看起来无用,但是查了一下发现用处还是挺大的:

ArrayList trimToSize() 的优点

我们知道 ArrayList 的容量是动态变化的。那么使用 ArrayList trimToSize() 方法的好处是什么?
要了解 trimToSize() 方法的优势,我们需要研究 ArrayList 内部的工作原理。
ArrayList 的内部使用数组存储元素,当数组将被存满,就会创建一个新数组,其容量是当前数组的 1.5 倍。
同时,所有元素都将移至新数组,假设内部数组已满,而我们现在又添加了 1 个元素,ArrayList 容量就会以相同的比例扩展(即前一个数组的1.5倍)。
在这种情况下,内部数组中将有一些未分配的空间。
这时,trimToSize() 方法可以删除未分配的空间并更改 ArrayList 的容量,使其等于 ArrayList 中的元素个数。

/*** Trims the capacity of this {@code ArrayList} instance to be the* list's current size.  An application can use this operation to minimize* the storage of an {@code ArrayList} instance.*/public void trimToSize() {modCount++;if (size < elementData.length) {elementData = (size == 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);}}

ArrayList.grow()方法

该方法最主要是确定该ArrayList能放的下你所准备存储的容量。
这里主要是用了Arrays.copyOf(elementData, newCapacity(minCapacity));去实现,其实也是重新构造了一个数组。

/*** Increases the capacity to ensure that it can hold at least the* number of elements specified by the minimum capacity argument.** @param minCapacity the desired minimum capacity* @throws OutOfMemoryError if minCapacity is less than zero*/private Object[] grow(int minCapacity) {return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));}private Object[] grow() {return grow(size + 1);}
/*** Returns a capacity at least as large as the given minimum capacity.* Returns the current capacity increased by 50% if that suffices.* Will not return a capacity greater than MAX_ARRAY_SIZE unless* the given minimum capacity is greater than MAX_ARRAY_SIZE.** @param minCapacity the desired minimum capacity* @throws OutOfMemoryError if minCapacity is less than zero*/private int newCapacity(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);if (newCapacity - minCapacity <= 0) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)return Math.max(DEFAULT_CAPACITY, minCapacity);if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return minCapacity;}return (newCapacity - MAX_ARRAY_SIZE <= 0)? newCapacity: hugeCapacity(minCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE)? Integer.MAX_VALUE: MAX_ARRAY_SIZE;}

这里是上面使用到的Arrays.copyOf(elementData, newCapacity(minCapacity));的源码

 @SuppressWarnings("unchecked")public static <T> T[] copyOf(T[] original, int newLength) {//此处为Arrays包内里面的return (T[]) copyOf(original, newLength, original.getClass());}@HotSpotIntrinsicCandidatepublic 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;}

本文用的是jdk12,当然我们现在生产上用的jdk 8 用的源码是下面这样的,对比一下,我们我们发现jdk 12是已经将jdk 8里面的条件进行了封装并且加多了内存泄露的异常捕获,所以这点是我们平时使用jdk 8 的时候需要注意到的地方。

private void grow(int minCapacity) {// overflow-conscious codeint 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);}

ArrayList判空

ArrayList的判空我们看下其实就是校验集合的size长度,所以我们平时校验空集合可以使用ArrayList.isEmpty()或者ArrayList.size( ) == 0这两种方式。

 /*** Returns the number of elements in this list.** @return the number of elements in this list*/public int size() {return size;}/*** Returns {@code true} if this list contains no elements.** @return {@code true} if this list contains no elements*/public boolean isEmpty() {return size == 0;}

ArrayList.indexOf(Object o)与lastIndexOf(Object o)

这是正序和倒序寻找存在目标元素的第一个索引。看下面源码可以看到源码使用了很简单的,用for循环去遍历的。但是对比JDK 12 与JDK 8可以发现,JDK12里面,将JDK 8的方法进行了小小的优化,使寻找目标元素索引的方法更加的灵活。

//jdk12/*** Returns the index of the first occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the lowest index {@code i} such that* {@code Objects.equals(o, get(i))},* or -1 if there is no such index.*/public int indexOf(Object o) {return indexOfRange(o, 0, size);}int indexOfRange(Object o, int start, int end) {Object[] es = elementData;if (o == null) {for (int i = start; i < end; i++) {if (es[i] == null) {return i;}}} else {for (int i = start; i < end; i++) {if (o.equals(es[i])) {return i;}}}return -1;}/*** Returns the index of the last occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the highest index {@code i} such that* {@code Objects.equals(o, get(i))},* or -1 if there is no such index.*/public int lastIndexOf(Object o) {return lastIndexOfRange(o, 0, size);}int lastIndexOfRange(Object o, int start, int end) {Object[] es = elementData;if (o == null) {for (int i = end - 1; i >= start; i--) {if (es[i] == null) {return i;}}} else {for (int i = end - 1; i >= start; i--) {if (o.equals(es[i])) {return i;}}}return -1;}//jdk 8
/*** Returns the index of the first occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the lowest index <tt>i</tt> such that* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,* or -1 if there is no such index.*/public int indexOf(Object o) {if (o == null) {for (int i = 0; i < size; i++)if (elementData[i]==null)return i;} else {for (int i = 0; i < size; i++)if (o.equals(elementData[i]))return i;}return -1;}/*** Returns the index of the last occurrence of the specified element* in this list, or -1 if this list does not contain the element.* More formally, returns the highest index <tt>i</tt> such that* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,* or -1 if there is no such index.*/public int lastIndexOf(Object o) {if (o == null) {for (int i = size-1; i >= 0; i--)if (elementData[i]==null)return i;} else {for (int i = size-1; i >= 0; i--)if (o.equals(elementData[i]))return i;}return -1;}

ArrayList.contains(Object o)

这个包含方法也是我们在开发中经常使用到的,我们看源码,它的实现其实也是用的寻找目标元素的索引是否存在。

/*** Returns {@code true} if this list contains the specified element.* More formally, returns {@code true} if and only if this list contains* at least one element {@code e} such that* {@code Objects.equals(o, e)}.** @param o element whose presence in this list is to be tested* @return {@code true} if this list contains the specified element*/public boolean contains(Object o) {return indexOf(o) >= 0;}

ArrayList.clone()

顾名思义,克隆目标集合,但是这里需要注意的是,克隆是浅拷贝的,如果目标集合的内存存储元素变量,这里也会改变。


/*** Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The* elements themselves are not copied.)** @return a clone of this <tt>ArrayList</tt> instance*/public Object clone() {try {ArrayList<?> v = (ArrayList<?>) super.clone();v.elementData = Arrays.copyOf(elementData, size);v.modCount = 0;return v;} catch (CloneNotSupportedException e) {// this shouldn't happen, since we are Cloneablethrow new InternalError(e);}}

ArrayList.toArray()与toArray(T[] a)

这是将ArrayList转换成数组这个方法比较简单,这里需要注意的是,前者的返回类型是Object,后者返回类型是泛类,所以这点大家在使用的时候需要注意一下返回类型。不然汇报异常转换错误:java.lang.ClassCastException,如果需要返回指定类型用后者就可以。下面直接看源码:

 /*** Returns an array containing all of the elements in this list* in proper sequence (from first to last element).** <p>The returned array will be "safe" in that no references to it are* maintained by this list.  (In other words, this method must allocate* a new array).  The caller is thus free to modify the returned array.** <p>This method acts as bridge between array-based and collection-based* APIs.** @return an array containing all of the elements in this list in*         proper sequence*/public Object[] toArray() {return Arrays.copyOf(elementData, size);}/*** Returns an array containing all of the elements in this list in proper* sequence (from first to last element); the runtime type of the returned* array is that of the specified array.  If the list fits in the* specified array, it is returned therein.  Otherwise, a new array is* allocated with the runtime type of the specified array and the size of* this list.** <p>If the list fits in the specified array with room to spare* (i.e., the array has more elements than the list), the element in* the array immediately following the end of the collection is set to* {@code null}.  (This is useful in determining the length of the* list <i>only</i> if the caller knows that the list does not contain* any null elements.)** @param a the array into which the elements of the list are to*          be stored, if it is big enough; otherwise, a new array of the*          same runtime type is allocated for this purpose.* @return an array containing the elements of the list* @throws ArrayStoreException if the runtime type of the specified array*         is not a supertype of the runtime type of every element in*         this list* @throws NullPointerException if the specified array is null*/@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}

这里我写了几行代码演示一下执行后的结果如下:

总结

由于篇幅原因,本文太长大家不好吸收,我将ArrayList分开成两个文章发出来,大家学习完这一篇再看下一篇的时候可能会更好理解和吸收。
对于本片的总结,用提问的方式来总结吧!
1、以下哪个方法用到的不是浅拷贝()
A、addAll(Collection<? extends E> c) B、clone() C、Arrays.copyOf(elementData, size) D、add(E e)
2、以下代码执行结果是()?

import java.util.ArrayList;public class ArrayListDemo {@SuppressWarnings({ "unchecked", "rawtypes" })public static void main(String[] args) {ArrayList a = new ArrayList();ArrayList b = new ArrayList();String[] c = new String[10];a.add("1");a.add("2");a.add("3");a.add("4");a.add("2");a.add("5");b.add("2");c = (String[]) a.toArray();for(int i = 0;i < c.length;i++)System.out.print(c[i]);}}

A、123425 B、编译报错 C、2

3、下列程序运行结果是

import java.util.ArrayList;public class ArrayListDemo {@SuppressWarnings({ "unchecked", "rawtypes" })public static void main(String[] args) {ArrayList<Integer> a1 = new ArrayList<Integer>();ArrayList<Integer> a2 = new ArrayList<Integer>();a1.add(1);a1.add(2);a1.add(3);a1.add(4);a2.add(11);a2.add(12);a2.add(13);ArrayList<ArrayList<Integer>> a = new ArrayList<ArrayList<Integer>>();a.add(a1);a.add(a2);ArrayList<ArrayList<Integer>> b = (ArrayList<ArrayList<Integer>>) a.clone();a2.remove(2);a2.remove(1);System.out.println(b);}}

A、编译报错 B、[[1, 2, 3, 4], [11]] C、[[1, 2, 3, 4], [11,12,13]]

以上内容如果有问题欢迎大家评论区留言或者私信。

ArrayList源码详细解析(一)相关推荐

  1. 超详细!ArrayList源码图文解析

    不诗意的女程序媛不是好厨师~ 转载请注明出处,From李诗雨-[https://blog.csdn.net/cjm2484836553/article/details/104329665] <超 ...

  2. spark word2vec 源码详细解析

    spark word2vec 源码详细解析 简单介绍spark word2vec skip-gram 层次softmax版本的源码解析 word2vec 的原理 只需要看层次哈弗曼树skip-gram ...

  3. Java 集合系列(2): ArrayList源码深入解析和使用示例

    戳上面的蓝字关注我们哦! 精彩内容 精选java等全套视频教程 精选java电子图书 大数据视频教程精选 java项目练习精选 概要 上一章,我们学习了Collection的架构.这一章开始,我们对C ...

  4. Hadoop HDFS创建文件/写数据流程、源码详细解析

    HDFS创建文件/写数据源码解析 HDFS HDFS写流程 创建文件源码 客户端 DistributedFileSystem DFSClient DFSOutputStream 客户端/Namenod ...

  5. 20行Python代码爬取2W多条音频文件素材【内附源码+详细解析】新媒体创作必备

    大家好,我是辣条. 今天的内容稍显简单,不过对于新媒体创作的朋友们还是很有帮助的,你能用上的话记得给辣条三连! 爬取目标 网站:站长素材 工具使用 开发工具:pycharm 开发环境:python3. ...

  6. JAVA8 LinkedList 链表源码详细解析

    今天突发奇想看了一下LinkedList的源码,还挺有趣的,话不多说,show me the code. 我使用的是IDEA,快捷键仅供参考. 按住Ctrl再点击类名可以进入类的源码,随便写一个含有L ...

  7. golang mutex源码详细解析

    目前golang的版本是1.12,其中的mutex是增加了普通模式和饥饿模式切换的优化版本,为了便于理解,这里先从上一个版本1.7版本的mutex开始分析,以后再对优化版本进行说明. Mutex结构说 ...

  8. Faster_R_CNN源码详细解析

    Faster R-CNN整体架构 首先使用共享卷积层为全图提取特征feature maps 将得到的feature maps送入RPN,RPN会产生接近两千个候选框proposals RoI Pool ...

  9. 火车轨道铁路轨道检测识别(附带Python源码+详细解析)

    现在的网络上,铁轨检测的源码几乎没有,所以自己参照着一篇汽车车道线检测的方法,然后调节参数,实现了铁轨的轨道检测,但现在只能检测直线,弯曲的铁轨检测下一步会实现,实现之后会更新的,敬请期待. 弯轨检测 ...

最新文章

  1. 191. Number of 1 Bits
  2. java内存块_JVM上的并发和Java内存模型之同步块笔记
  3. vc++ List Control控件获得所有选中行的序号
  4. 用js使得输入框input只能输入数字
  5. 批量修改txt内容_不用再慢慢手动修改,用Excel批量修改文件名
  6. 9999元起!荣耀首部折叠屏手机Magic V正式发布
  7. 【安装包】XMind-ZEN-Update-2019-for-Windows-64bit-9.2.1
  8. 深入浅出Symfony2 - 结合MongoDB开发LBS应用
  9. 栅栏布局合并html,制作简约CSS栅栏布局
  10. 基于QT的英文文献的编辑与检索系统的实现
  11. excel怎么启用宏_怎么使用Excel制作条形码?操作如此简单
  12. webshell一句话
  13. RecyclerView clipToPadding
  14. Excel技巧 - 换行符用法
  15. 数据结构—排序二叉树
  16. nginx 地址重写
  17. 盘符没有显示,磁盘管理器提示磁盘没有初始化(已解决)
  18. 华为智慧屏鸿蒙os系统体验,华为智慧屏S Pro体验:告诉你鸿蒙OS有多优秀?
  19. 新浪新闻评论系统的架构演进和经验总结
  20. pve 加大local容量_一步一步实现Proxmox (pve)环境的家庭服务器(WIN,DSM,软路由)...

热门文章

  1. xiaomi 小米 红米redmi 秒解锁BL锁,不用等,在线秒解锁BL工具介绍
  2. Django中的ORM模型
  3. 宝爸的5A学习心得-1909期PMP分享
  4. mongodb 搜索速度_MongoDB 模糊查询慢的问题 以及相关解决方案的探索
  5. AI时代的视频云转码移动端化——更快、更好,更低,更广
  6. PHP云转码安装教程
  7. github前端面试题1
  8. CV5200自组网远程WiFi模组,无人机无线图传应用,高清低时延方案
  9. ASC认证|水产ASC标签正逐步进入国人视野
  10. ABAP 前台执行和后台执行