数组拷贝

基本数据类型 和 引用数据类型存储

基本数据类型有哪些,number,string,boolean,null,undefined五类。

引用数据类型(Object类)有常规名值对的无序对象{a:1},数组[1,2,3],以及函数等。

这两类数据存储分别是这样的:

a.基本类型–名值存储在栈内存中,例如int a=1;

img

当你b=a复制时,栈内存会新开辟一个内存,例如这样:

img

所以当你此时修改a=2,对b并不会造成影响,因为此时的b已自食其力,翅膀硬了,不受a的影响了。

当然,int a=1,b=a;虽然b不受a影响,但这也算不上深拷贝,因为深拷贝本身只针对较为复杂的Object类型数据。

b.引用数据类型–名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值,我们以上面浅拷贝的例子画个图:

img

当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。

img

而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。

img

那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,起步就达到深拷贝的效果了。

img

数组拷贝

 数组拷贝即为数组复制。首先,可以分为浅拷贝深拷贝
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象

浅拷贝是按位拷贝对象:

①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。

②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

 目前在Java中数据拷贝提供了如下方式:

1. for 循环(浅拷贝)
2. Object.clone()	克隆拷贝 (浅拷贝)
3. System.arraycopy()  (浅拷贝)
4. Arrays.copyOf()  (浅拷贝)

for 循环实现数组拷贝

class TestArray {
    private int val = 10;
    public void setVal(int val) { this.val = val; }
    public int getVal() { return this.val; }
}
public class Arraysort {
    public static void main(String[] args) {
        TestArray[] barry1 = new TestArray[4];
        barry1[0] = new TestArray();
        barry1[1] = new TestArray();
        barry1[2] = new TestArray();
        barry1[3] = new TestArray();
        TestArray[] barry2 = new TestArray[4];
        for (int i = 0; i < barry1.length; i++) {
            barry2[i] = barry1[i];
        }
        for (int i = 0; i < barry1.length; i++) {
            System.out.print(barry1[i].getVal() + " ");
        }
        System.out.println();
        for (int i = 0; i < barry2.length; i++) {
            System.out.print(barry2[i].getVal() + " ");
        }
        System.out.println();
        barry2[0].setVal(100000);
        System.out.println("===============");

        for (int i = 0; i < barry1.length; i++) {
            System.out.print(barry1[i].getVal() + " ");
        }
        System.out.println();
        for (int i = 0; i < barry2.length; i++) {
            System.out.print(barry2[i].getVal() + " ");
        }
    }

}
10 10 10 10 
10 10 10 10 
===============
100000 10 10 10 
100000 10 10 10   //浅拷贝

 由上可得,for循环是浅拷贝,产生一个新的对象,两个引用指向同一个对象,这里即是引用类型拷贝内存地址,如果有一个对象改变了地址,会影响到另外一个对象,代码灵活,但效率低,速度相对较慢。

克隆拷贝 Object.clone()

 在Java语言中,数组是引用类型。如果有两个数组变量a1和a2,则语句“a2 = a1;”将数组变量a1的引用传递给另一个数组a2;如果数组a2发生变化,则数组a1也发生变化。对于引用类型来说,它是浅拷贝;对简单类型来说,它是深拷贝。
引用类型:

package kkee;
import java.util.Arrays;
public class Arraysort {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7,8,9};
        int[] barry = array.clone();
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(barry));
        barry[0] = 1000;
        System.out.println("=================");
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(barry));
    }
}
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9]   //深拷贝
class TestArray {
    private int val = 10;
    public void setVal(int val) { this.val = val; }
    public int getVal() { return this.val; }
}
public class Arraysort {
    public static void main(String[] args) {
        TestArray[] t1 = new TestArray[4];
        t1[0] = new TestArray();
        t1[1] = new TestArray();
        t1[2] = new TestArray();
        t1[3] = new TestArray();
        TestArray[] t2 = t1.clone();//t2[0]
        for(int i = 0;i < t1.length;i++) {
            System.out.print(t1[i].getVal()+" ");
        }
        System.out.println();
        for(int i = 0;i < t2.length;i++) {
            System.out.print(t2[i].getVal()+" ");
        }
        System.out.println();
        t2[0].setVal(100000);
        System.out.println("===============");

        for(int i = 0;i < t1.length;i++) {
            System.out.print(t1[i].getVal()+" ");
        }
        System.out.println();
        for(int i = 0;i < t2.length;i++) {
            System.out.print(t2[i].getVal()+" ");
        }
    }
10 10 10 10 
10 10 10 10 
===============
100000 10 10 10 
100000 10 10 10   //浅拷贝

 查看clone()的源代码可知:

 protected native Object clone() throws CloneNotSupportedException;

它的关键字是native(本地方法),返回类型是Object,所以赋值时要发生强制转换,并且也是由底层的C/C++语言实现的。

Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。

但是需要注意:

1、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。因为protected关键字只允许同一个包内的类和它的子类调用,所以我们声明一个object类时,肯定不是同一个包内,所以就不能去调用它。

2、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。

要调用这个方法,就需要我们写一个类,然后声明实现cloneable接口就好了,重写clone()方法:

@Override
    public Object clone() throws CloneNotSupportedException {
        // TODO Auto-generated method stub
        return super.clone();
    }

这里需要是,为了能够在不同包内去调用这个方法,我们需要把这个权限升级为public。现在我们就可以调用这个类的clone()方法去拷贝我们的类了。总得来说clone()就是浅拷贝。s

System.arraycopy()

 System.arraycopy(src,srcPos,dest,destPos,length)是System类提供的一个静态方法,可以用它来实现数组之间的赋值。查看源代码:

public static native void arraycopy(Object src, int srcPos, Object dest, int desPos, int length)
//参数含义:(原数组, 原数组的开始位置, 目标数组, 目标数组的开始位置, 拷贝个数)

它是浅拷贝,也就是说对于非基本类型而言,它拷贝的是对象的引用,而不是去新建一个新的对象。通过它的代码我们可以看到,这个方法不是用java语言写的,而是底层用c或者c++实现的,因而速度会比较快。

public class Arraysort {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7,8,9};
        int[] brray = new int[array.length];
        System.arraycopy(array,0,brray,0,array.length);
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(brray));
        brray[0] = 1000;
        System.out.println("=================");
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(brray));
   }
 }
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9]
class TestArray {
    private int val = 10;
    public void setVal(int val) { this.val = val; }
    public int getVal() { return this.val; }
}
public class Arraysort {
 public static void main(String[] args) {
        TestArray[] t1 = new TestArray[4];
        t1[0] = new TestArray();
        t1[1] = new TestArray();
        t1[2] = new TestArray();
        t1[3] = new TestArray();
        TestArray[] t2 = new TestArray[4];

        System.arraycopy(t1,0,t2,0,t1.length);

        for(int i = 0;i < t1.length;i++) {
            System.out.print(t1[i].getVal()+" ");
        }
        System.out.println();
        for(int i = 0;i < t2.length;i++) {
            System.out.print(t2[i].getVal()+" ");
        }
        System.out.println();
        t2[0].setVal(100000);
        System.out.println("===============");

        for(int i = 0;i < t1.length;i++) {
            System.out.print(t1[i].getVal()+" ");
        }
        System.out.println();
        for(int i = 0;i < t2.length;i++) {
            System.out.print(t2[i].getVal()+" ");
        }
    }
10 10 10 10 
10 10 10 10 
===============
100000 10 10 10 
100000 10 10 10 

Arrays.copyOf()

 Arrays.copyOf底层其实也是用的System.arraycopy ,所以效率自然低于System.arraycpoy(),所以它也是浅拷贝。
源码如下:

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 class Arraysort {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7,8,9};
        int[] brray = Arrays.copyOf(array,array.length);

        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(brray));
        brray[0] = 1000;
        System.out.println("=================");
        System.out.println(Arrays.toString(array));
        System.out.println(Arrays.toString(brray));
    }
    }
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
=================
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1000, 2, 3, 4, 5, 6, 7, 8, 9]
Arrays.copyOfRange():

复制数组的指定范围内容。源代码:

public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
        int newLength = to - from;
        if (newLength < 0)
            throw new IllegalArgumentException(from + " > " + to);
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, from, copy, 0,
                         Math.min(original.length - from, newLength));
        return copy;
    }

copyOfRange(数组名,起始索引,结束索引)从起始索引(包括)到结束索引(不包括)。结果为相同数据类型的数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值