java Android List深拷贝的两种方式

作者: android 发布时间: 2019-08-05 浏览: 6362 次 编辑

大家都知道java里面分为浅拷贝和深拷贝。举个简单的例子,区分一下浅拷贝和深拷贝的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Address{
  
    private String address;
  
    public Address(String address){
        this.address = address;
    }
  
    public String getAddress(){
        return address;
    }
  
    public void setAddress(String address){
        this.address = address;
    }
  
    @Override
    protected Object clone() {
        Address address = null;
        try {
            address = (Address) super.clone();
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return address;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Student implements Cloneable{
  
    private String name;
    public List<Address> addressList;
  
    public Student(){
    }
  
    public Student(String name) {
        this.name = name;
    }
  
    public void setName(String name){
        this.name = name;
    }
  
    public String getName() {
        return name;
    }
  
    @Override
    protected Object clone() {
        Student student = null;
        try {
            student = (Student) super.clone();
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }
}

先创建两个类,一个是地址,一个是学生,便于后边的打印结果能明显区分浅拷贝和深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class myClass {
  
    public static void main(String[] args) {
        Student student = new Student("李晓东");
        Student student2 = (Student) student.clone();
  
        println(student.getName() );
        println(student2.getName() );
        println("改变student2的姓名后--------------");
        student2.setName("张天");
        println(student.getName() );
        println(student2.getName());
    }
  
    public static void println(String str) {
        System.out.println(str);
    }
  
  
}


打印结果如下

1
2
3
4
5
李晓东
李晓东
改变student2的姓名后--------------
李晓东
张天

可以看到当我们改变student2的name值的时候,并没有改变student的name值(String在此时不属于引用值类型)

在我们的项目当中,经常会遇到一个类里面有List,然后List装载另外一个对象,这个时候要进行深拷贝就需要用到如下的办法,先把Address进行序列化和实现cloneable接口并且重写clone方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Address implements Cloneable,Serializable{
  
    private String address;
  
    public Address(String address){
        this.address = address;
    }
  
    public String getAddress(){
        return address;
    }
  
    public void setAddress(String address){
        this.address = address;
    }
  
    @Override
    protected Object clone() {
        Address address = null;
        try {
            address = (Address) super.clone();
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return address;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class myClass {
  
    public static void main(String[] args) {
        List<Address> addressList = new ArrayList<>();
        addressList.add(new Address("北京市"));
        Student student = new Student("李晓东");
        student.addressList = addressList;
  
        Student student2 = (Student) student.clone();
        student2.addressList = depCopy2(addressList);
  
        println(student.getName() + "---" + student.addressList.get(0).getAddress());
        println(student2.getName() + "---" + student2.addressList.get(0).getAddress());
        println("改变student2的姓名后--------------");
        student2.setName("张天");
        student2.addressList.get(0).setAddress("湖南省");
        println(student.getName() + "---" + student.addressList.get(0).getAddress());
        println(student2.getName() + "---" + student2.addressList.get(0).getAddress());
    }
  
    public static void println(String str) {
        System.out.println(str);
    }
  
    /***
     * 方法二
     * 需要Address实现cloneable接口和重写clone方法,次方法有限制性,
     * 例如要先声明List是保存的什么对象,并且当碰到对象里面还持有List集合的时候
     * 就不管用的,所以建议使用第一种方法
     
     * @param addresses
     * @return
     */
    public static List<Address> depCopy2(List<Address> addresses) {
        List<Address> destList = new ArrayList<>();
        for (Address address : addresses) {
            destList.add((Address) address.clone());
        }
        return destList;
    }
  
    /***
     * 方法一对集合进行深拷贝 注意需要对泛型类进行序列化(实现Serializable)
     
     * @param srcList
     * @param <T>
     * @return
     */
    public static <T> List<T> depCopy(List<T> srcList) {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        try {
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(srcList);
  
            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream inStream = new ObjectInputStream(byteIn);
            List<T> destList = (List<T>) inStream.readObject();
            return destList;
        catch (IOException e) {
            e.printStackTrace();
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
  
}

此时打印结果是

1
2
3
4
5
李晓东---北京市
李晓东---北京市
改变student2的姓名后--------------
李晓东---北京市
张天---湖南省

如果没看明白,可以自己修改不实现cloneable接口和注释掉clone方法再执行看结果,一定要自己写自己试,这样才会印象深刻!!!