上周有朋友问我ArrayList的构造函数ArrayList(Collection extends E> c)是浅拷贝还是深拷贝,当时一下子也没反应过来,就写了一个单元测试验证了一下,结果是浅拷贝,代码如下:(浅拷贝的原理可以参考这篇文章《java浅拷贝和深拷贝》)
public void testAr(){
List beanDemos = new ArrayList<>();
BeanDemo beanDemo = new BeanDemo();
beanDemo.setName("test1");
beanDemos.add(beanDemo);
List beanDemoList = new ArrayList<>(beanDemos);
beanDemo.setName("test2");
System.out.println(beanDemoList.get(0).getName());
//结果输出test2
}
现在深入研究一下源码,为什么ArrayList(Collection extends E> c)构造函数是浅拷贝。ArrayList构造函数源码如下:
/**
* 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) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
构造函数首先把集合类型的参数转为数组(ArrayList底层实现是数组,LinkedList底层实现是列表),然后调用了Arrays.copyof()方法,传参是原始数组和拷贝数组的长度,copyof()通过获取一个原始数组的副本,被截断或用null填充以返回指定的长度。源码如下,到这一步还看不出是到底是浅拷贝还是深拷贝:
public static 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;
}
copyof()方法中主要使用System.arraycopy()方法来实现列表拷贝,再往下查看System.arraycopy(),发现这个是一个原生native的函数(native方法不是用java实现的),我只能仔细阅读一下这个原生方法的注释,参见文章最下方,注释内容比较长,但第一段注释直接告诉了我们arraycopy()方法拷贝的是数组的引用地址,所以它属于浅拷贝:A subsequence of array components are copied from the source array referenced by src
to the destination array referenced by dest
.
综上:ArrayList的构造函数ArrayList(Collection extends E> c)是一种浅拷贝。
/**
* Copies an array from the specified source array, beginning at the
* specified position, to the specified position of the destination array.
* A subsequence of array components are copied from the source
* array referenced by src
to the destination array
* referenced by dest
. The number of components copied is
* equal to the length
argument. The components at
* positions srcPos
through
* srcPos+length-1
in the source array are copied into
* positions destPos
through
* destPos+length-1
, respectively, of the destination
* array.
*
* If the src
and dest
arguments refer to the
* same array object, then the copying is performed as if the
* components at positions srcPos
through
* srcPos+length-1
were first copied to a temporary
* array with length
components and then the contents of
* the temporary array were copied into positions
* destPos
through destPos+length-1
of the
* destination array.
*
* If dest
is null
, then a
* NullPointerException
is thrown.
*
* If src
is null
, then a
* NullPointerException
is thrown and the destination
* array is not modified.
*
* Otherwise, if any of the following is true, an
* ArrayStoreException
is thrown and the destination is
* not modified:
*
* - The
src
argument refers to an object that is not an
* array.
* - The
dest
argument refers to an object that is not an
* array.
* - The
src
argument and dest
argument refer
* to arrays whose component types are different primitive types.
* - The
src
argument refers to an array with a primitive
* component type and the dest
argument refers to an array
* with a reference component type.
* - The
src
argument refers to an array with a reference
* component type and the dest
argument refers to an array
* with a primitive component type.
*
*
* Otherwise, if any of the following is true, an
* IndexOutOfBoundsException
is
* thrown and the destination is not modified:
*
* - The
srcPos
argument is negative.
* - The
destPos
argument is negative.
* - The
length
argument is negative.
* srcPos+length
is greater than
* src.length
, the length of the source array.
* destPos+length
is greater than
* dest.length
, the length of the destination array.
*
*
* Otherwise, if any actual component of the source array from
* position srcPos
through
* srcPos+length-1
cannot be converted to the component
* type of the destination array by assignment conversion, an
* ArrayStoreException
is thrown. In this case, let
* k be the smallest nonnegative integer less than
* length such that src[srcPos+
k]
* cannot be converted to the component type of the destination
* array; when the exception is thrown, source array components from
* positions srcPos
through
* srcPos+
k-1
* will already have been copied to destination array positions
* destPos
through
* destPos+
k-1
and no other
* positions of the destination array will have been modified.
* (Because of the restrictions already itemized, this
* paragraph effectively applies only to the situation where both
* arrays have component types that are reference types.)
*
* @param src the source array.
* @param srcPos starting position in the source array.
* @param dest the destination array.
* @param destPos starting position in the destination data.
* @param length the number of array elements to be copied.
* @exception IndexOutOfBoundsException if copying would cause
* access of data outside array bounds.
* @exception ArrayStoreException if an element in the src
* array could not be stored into the dest
array
* because of a type mismatch.
* @exception NullPointerException if either src
or
* dest
is null
.
*/
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);