Java Arrays.copyof方法

802 阅读3分钟

arrays.copyof方法到底是深度复制还是浅度复制; 我们做一个实验: 编写测试类:

package Copy;

import java.util.Arrays;

/**
 * @Author: sunsuhai
 * @Date: 2018/11/18 17:42
 */
public class Copy {

    public static void doSomeing(){

        int[] a = new int[5];
        int[] b = new int[5];
        System.out.println("a数组的地址:"+a);
        System.out.println("b数组的地址:"+b);
        int[] c = Arrays.copyOf(a,5);
        System.out.println("c数组的地址:"+c);
    }

    public static void doSomeing2(){
        SunShuai[] a = new SunShuai[5];
        SunShuai[] b = Arrays.copyOf(a,5);

        System.out.println("a数组的地址:"+a);
        System.out.println("b数组的地址:"+b);

        SunShuai sunShuai = new SunShuai(1,"123");
        a[0] = sunShuai;
        SunShuai[] c = Arrays.copyOf(a,5);
        System.out.println("a数组的地址:"+a);
        System.out.println("c数组的地址:"+c);
        System.out.println("a[0]元素的地址:"+a[0]);
        System.out.println("c[0]元素的地址:"+c[0]);
        System.out.println("a[0]元素的地址:"+a[0]);
        System.out.println("c[0]元素的地址:"+c[0]);
        a[0].setInfo("456");
        a[0].setCode(2);
        System.out.println("a[0]元素的内容:"+a[0]);
        System.out.println("c[0]元素的内容:"+c[0]);
    }

    public static void main(String[] args) {
        //doSomeing();
        doSomeing2();
    }
}

class SunShuai{
    private int code;
    private String info;

    public SunShuai(int code, String info) {
        this.code = code;
        this.info = info;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

//    @Override
//    public String toString() {
//        return "SunShuai{" +
//                "code=" + code +
//                ", info='" + info + '\'' +
//                '}';
//    }
}

输出结果为:

a数组的地址:[LCopy.SunShuai;@5a07e868
b数组的地址:[LCopy.SunShuai;@76ed5528
a数组的地址:[LCopy.SunShuai;@5a07e868
c数组的地址:[LCopy.SunShuai;@2c7b84de
a[0]元素的地址:Copy.SunShuai@3fee733d
c[0]元素的地址:Copy.SunShuai@3fee733d
a[0]元素的地址:Copy.SunShuai@3fee733d
c[0]元素的地址:Copy.SunShuai@3fee733d
a[0]元素的内容:Copy.SunShuai@3fee733d
c[0]元素的内容:Copy.SunShuai@3fee733d

我们可以看到,数组的值并不改变; 点进arrays.copyof的源码我们可以看到

 @SuppressWarnings("unchecked")
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }

关于@suppressWarning的解释可以看上一篇笔记 @suppressWarning 他调用了内部的copyof方法,多传递了一个对象类型

我们再看copyof方法

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;
    }

创建一个新的数组,然后调用system.arraycopy方法;

graph LR
a --> SunShuai-0
a --> SunShuai-1
a --> b
b --> Sunshuai-0-0
b --> Sunshuai-0-1
Sunshuai-0-0 --> SunShuai-0
Sunshuai-0-1 --> SunShuai-1

System.arraycopy分析

static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst,
                             jint dstPos, jint length) {
 
  const jint count = length;
  ScopedFastNativeObjectAccess soa(env);

  // 目标数组和源数组的空值检验
  if (UNLIKELY(javaSrc == nullptr)) {
    ThrowNullPointerException("src == null");
    return;
  }
  if (UNLIKELY(javaDst == nullptr)) {
    ThrowNullPointerException("dst == null");
    return;
  }

  // 确保两个对象都是数组
  ObjPtr<mirror::Object> srcObject = soa.Decode<mirror::Object>(javaSrc);
  if (UNLIKELY(!srcObject->IsArrayInstance())) {
    ThrowArrayStoreException_NotAnArray("source", srcObject);
    return;
  }
  ObjPtr<mirror::Object> dstObject = soa.Decode<mirror::Object>(javaDst);
  if (UNLIKELY(!dstObject->IsArrayInstance())) {
    ThrowArrayStoreException_NotAnArray("destination", dstObject);
    return;
  }
  ObjPtr<mirror::Array> srcArray = srcObject->AsArray();
  ObjPtr<mirror::Array> dstArray = dstObject->AsArray();

  // 数组越界检查
  if (UNLIKELY(srcPos < 0) || UNLIKELY(dstPos < 0) || UNLIKELY(count < 0) ||
      UNLIKELY(srcPos > srcArray->GetLength() - count) ||
      UNLIKELY(dstPos > dstArray->GetLength() - count)) {
    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
                                   "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
                                   srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos,
                                   count);
    return;
  }

  ObjPtr<mirror::Class> dstComponentType = dstArray->GetClass()->GetComponentType();
  ObjPtr<mirror::Class> srcComponentType = srcArray->GetClass()->GetComponentType();
  Primitive::Type dstComponentPrimitiveType = dstComponentType->GetPrimitiveType();

  if (LIKELY(srcComponentType == dstComponentType)) {
    // Trivial assignability.
    switch (dstComponentPrimitiveType) {
    //我们可以看到,如果是基本类型,会调用memmove方法
      case Primitive::kPrimVoid:
        LOG(FATAL) << "Unreachable, cannot have arrays of type void";
        UNREACHABLE();
      case Primitive::kPrimBoolean:
      case Primitive::kPrimByte:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 1U);
        dstArray->AsByteSizedArray()->Memmove(dstPos, srcArray->AsByteSizedArray(), srcPos, count);
        return;
      case Primitive::kPrimChar:
      case Primitive::kPrimShort:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 2U);
        dstArray->AsShortSizedArray()->Memmove(dstPos, srcArray->AsShortSizedArray(), srcPos, count);
        return;
      case Primitive::kPrimInt:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
        dstArray->AsIntArray()->Memmove(dstPos, srcArray->AsIntArray(), srcPos, count);
        return;
      case Primitive::kPrimFloat:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
        dstArray->AsFloatArray()->Memmove(dstPos, srcArray->AsFloatArray(), srcPos, count);
        return;
      case Primitive::kPrimLong:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
        dstArray->AsLongArray()->Memmove(dstPos, srcArray->AsLongArray(), srcPos, count);
        return;
      case Primitive::kPrimDouble:
        DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
        dstArray->AsDoubleArray()->Memmove(dstPos, srcArray->AsDoubleArray(), srcPos, count);
        return;
      case Primitive::kPrimNot: {
      //如果是对象类型,那么调用AssignableMemmove方法
        mirror::ObjectArray<mirror::Object>* dstObjArray = dstArray->AsObjectArray<mirror::Object>();
        mirror::ObjectArray<mirror::Object>* srcObjArray = srcArray->AsObjectArray<mirror::Object>();
        dstObjArray->AssignableMemmove(dstPos, srcObjArray, srcPos, count);
        return;
      }
      default:
        LOG(FATAL) << "Unknown array type: " << srcArray->PrettyTypeOf();
        UNREACHABLE();
    }
  }
  // If one of the arrays holds a primitive type the other array must hold the exact same type.
  if (UNLIKELY((dstComponentPrimitiveType != Primitive::kPrimNot) ||
               srcComponentType->IsPrimitive())) {
    std::string srcType(srcArray->PrettyTypeOf());
    std::string dstType(dstArray->PrettyTypeOf());
    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
                                   "Incompatible types: src=%s, dst=%s",
                                   srcType.c_str(), dstType.c_str());
    return;
  }
  // Arrays hold distinct types and so therefore can't alias - use memcpy instead of memmove.
  ObjPtr<mirror::ObjectArray<mirror::Object>> dstObjArray =
      dstArray->AsObjectArray<mirror::Object>();
  ObjPtr<mirror::ObjectArray<mirror::Object>> srcObjArray =
      srcArray->AsObjectArray<mirror::Object>();
  // If we're assigning into say Object[] then we don't need per element checks.
  if (dstComponentType->IsAssignableFrom(srcComponentType)) {
    dstObjArray->AssignableMemcpy(dstPos, srcObjArray, srcPos, count);
    return;
  }
  // This code is never run under a transaction.
  DCHECK(!Runtime::Current()->IsActiveTransaction());
  dstObjArray->AssignableCheckingMemcpy<false>(dstPos, srcObjArray, srcPos, count, true);
}