小问题4 | Arrays.copyOf(elementData, size, a.getClass())是如何实现的?

小问题4 | Arrays.copyOf(elementData, size, a.getClass())是如何实现的?

上一个问题(小问题2 | 为什么对一个List调用toArray(new String[0])就可以把List转换成对应的数组?)中我们看到:

List中的toArray方法,无论是public Object[] toArray(),还是重载的public T[] toArray(T[] a),在源码中都调用了Arrays.copyOf方法。

Arrays.copyOf(elementData, size, a.getClass())是如何实现的?

我们直接来看Arrays.copyOf方法的源码:

    // 以int类型数组为例,其他类型类似
	@SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

	@SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        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;
    }

在源码中,我们看到了一个清晰的调用结构,无论是从第一个方法进入还是从第二个方法进入,最终都是调用
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) 这个更具普适性的方法。

下面我们对该方法进行拆解:

首先,代码对传入的数组是否是Object类型数组进行判断:

T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);

如果原数组是Object类型数组,则直接创建一个给定的newLength长度的新Object数组(此处创建新数组的目的是,为了在下一步的操作中接收复制结果)

如果原数组不是Object类型数组,则调用 (T[]) Array.newInstance(newType.getComponentType(), newLength) 这个方法,新建一个数组,数组类型为newType中元素的类型(默认返回Object类型,可强制转换为正确类型),长度为给定的newLength。

而Array.newInstance方法内部,直接调用Array.newArray,newArray为本地方法,由虚拟机实现。

接着,代码调用System.arraycopy方法:

System.arraycopy(original, 0, copy, 0,
        Math.min(original.length, newLength));

在此简单说明一下该方法,根据java源码注释的翻译,对该方法参数的说明如下:

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos,  int length);
       @param      src      原数组
     * @param      srcPos   原数组开始位置
     * @param      dest      目标数组
     * @param      destPos  目标数组开始位置.
     * @param      length    复制的长度
     * @exception  IndexOutOfBoundsException 数组越界
     * @exception  ArrayStoreException   类型不匹配
     * @exception  NullPointerException  原数组或目标数组为Null

由注释我们可知,此处通过 System.arraycopy方法实现了原数组到新数组的复制,而这里的System.arraycopy方法是一个静态的native方法,可以使用这个方法来实现数组之间的复制,该方法实现了对数组的浅拷贝

什么是浅拷贝?

Java数组的复制操作可以分为深拷贝浅拷贝,简单来说**,深拷贝,指拷贝对象的值和对象的内容**;而浅拷贝,只是对对象引用内容的复制。

对于一维数组来说,这种复制属性值传递,修改副本不会影响原来的值。

对于二维或者一维数组中存放的是对象时,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。

简而言之,我个人的理解,这部分意味着——System.arraycopy只能复制一个维度的数据,二维及以上则是浅复制,只有在一维的是深复制。

是的,这里有两个新问题,当我们不想只是拷贝引用地址的时候——
小问题6 | Java中如何实现深拷贝?实现深拷贝的三种方法

另一个问题是,有朋友可能对刚刚提到的Native方法比较陌生——
小问题5 | 什么是native方法?附JNI介绍
当然,有兴趣的话,我们更能够研究一下如何在我们的代码中使用。

我们带着这些问题继续看。

————————————————————

本专栏是【小问题】系列,旨在每篇解决一个小问题,并秉持着刨根问底的态度解决这个问题可能带出的一系列问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值