浅拷贝之ArrayList构造函数源码解析/Syste.arraycopy()

上周有朋友问我ArrayList的构造函数ArrayList(Collection 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 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 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 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 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);

 

你可能感兴趣的:(java)