拷贝的核心差异,本质就是“是否复制堆内存中的真实对象”。
一、List浅层拷贝(浅拷贝):只拷贝容器和引用,不拷贝元素对象
1. 定义
浅拷贝会创建一个新的List容器对象,但新List中的元素只是原List元素的引用地址拷贝(栈内存的地址复制),堆内存中的元素对象本身并没有被复制。
简单说:新List和原List是两个不同的“篮子”,但里面装的是同一个“苹果”。
2. 浅拷贝的常见实现方式(源码示例)
先定义一个自定义引用类型(由于基本类型的List拷贝无深浅之分,只有引用类型才能体现差异):
// 自定义用户类(引用类型)
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getter/Setter(方便修改和查看属性)
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
接下来演示3种常见的List浅拷贝方式,并验证效果:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ListCopyDemo {
public static void main(String[] args) {
// 1. 初始化原List
List<User> originalList = new ArrayList<>();
originalList.add(new User("张三", 20));
originalList.add(new User("李四", 25));
System.out.println("原List初始值:" + originalList);
// 方式1:使用ArrayList的构造器(最常用)
List<User> shallowCopy1 = new ArrayList<>(originalList);
// 方式2:使用List的clone()方法(ArrayList重写了clone,本质也是浅拷贝)
List<User> shallowCopy2 = (List<User>) ((ArrayList<User>) originalList).clone();
// 方式3:使用Stream流拷贝
List<User> shallowCopy3 = originalList.stream().collect(Collectors.toList());
// 2. 修改浅拷贝List中的元素属性(以shallowCopy1为例)
shallowCopy1.get(0).setName("张三-修改后");
shallowCopy1.get(0).setAge(21);
// 3. 打印验证:原List和所有浅拷贝List的元素都被修改了
System.out.println("原List修改后:" + originalList);
System.out.println("浅拷贝1修改后:" + shallowCopy1);
System.out.println("浅拷贝2修改后:" + shallowCopy2);
System.out.println("浅拷贝3修改后:" + shallowCopy3);
}
}
3. 输出结果与核心解释
原List初始值:[User{name='张三', age=20}, User{name='李四', age=25}]
原List修改后:[User{name='张三-修改后', age=21}, User{name='李四', age=25}]
浅拷贝1修改后:[User{name='张三-修改后', age=21}, User{name='李四', age=25}]
浅拷贝2修改后:[User{name='张三-修改后', age=21}, User{name='李四', age=25}]
浅拷贝3修改后:[User{name='张三-修改后', age=21}, User{name='李四', age=25}]
核心缘由:
- 新List(shallowCopy1/2/3)是新创建的容器(堆内存中独立的List对象),但List中的每个User元素只是复制了引用地址(列如原List中第一个User的地址是0x123,浅拷贝后新List的第一个元素地址也是0x123);
- 当修改浅拷贝List中User的属性时,实际修改的是堆内存中0x123地址对应的User对象,因此原List和所有浅拷贝List都会看到这个变化。
二、List深层拷贝(深拷贝):拷贝容器+所有元素对象
1. 定义
深拷贝会创建一个新的List容器对象,同时递归地拷贝List中的每一个元素对象(在堆内存中创建全新的元素对象)。
简单说:新List和原List是不同的“篮子”,里面装的也是完全独立的“苹果”。
2. 深拷贝的实现方式(源码示例)
深拷贝需要让元素类支持“自身拷贝”,常见有3种方式,以下演示最通用的序列化方式(不受元素类结构限制):
第一步:让User类实现序列化接口(Serializable):
import java.io.Serializable;
// 实现Serializable接口,支持序列化/反序列化
class User implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号(必须)
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getter/Setter/toString 同上
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
第二步:实现深拷贝工具方法,并验证:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class ListDeepCopyDemo {
// 深拷贝工具方法:通过序列化+反序列化实现
@SuppressWarnings("unchecked")
public static <T extends Serializable> List<T> deepCopy(List<T> original) {
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
// 1. 将原List序列化(写入字节流)
oos.writeObject(original);
oos.flush();
// 2. 反序列化字节流,生成全新的List和元素对象
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
return (List<T>) ois.readObject();
}
} catch (Exception e) {
throw new RuntimeException("深拷贝失败", e);
}
}
public static void main(String[] args) {
// 1. 初始化原List
List<User> originalList = new ArrayList<>();
originalList.add(new User("张三", 20));
originalList.add(new User("李四", 25));
System.out.println("原List初始值:" + originalList);
// 2. 执行深拷贝
List<User> deepCopyList = deepCopy(originalList);
// 3. 修改深拷贝List中的元素属性
deepCopyList.get(0).setName("张三-修改后");
deepCopyList.get(0).setAge(21);
// 4. 打印验证:原List不变,深拷贝List独立变化
System.out.println("原List修改后:" + originalList);
System.out.println("深拷贝List修改后:" + deepCopyList);
}
}
3. 输出结果与核心解释
原List初始值:[User{name='张三', age=20}, User{name='李四', age=25}]
原List修改后:[User{name='张三', age=20}, User{name='李四', age=25}]
深拷贝List修改后:[User{name='张三-修改后', age=21}, User{name='李四', age=25}]
核心缘由:
- 序列化会将原List和其中的所有User对象转换成字节流,反序列化时会根据字节流重新创建全新的List对象和User对象(堆内存中独立的地址);
- 修改深拷贝List中的User属性,只会影响新创建的User对象,原List中的User对象完全不受影响。
补充:深拷贝的其他实现方式
- 元素类实现Cloneable接口:让User重写clone()方法,然后遍历原List,逐个clone元素并添加到新List;
- 手动new对象赋值:遍历原List,手动new User并复制属性,添加到新List(适合简单类)。
三、浅拷贝vs深拷贝核心区别对比
|
维度 |
浅层拷贝 |
深层拷贝 |
|
容器对象 |
新创建(独立) |
新创建(独立) |
|
元素对象 |
引用复用(同一个堆对象) |
全新创建(独立的堆对象) |
|
修改元素属性的影响 |
原List和拷贝List都会受影响 |
仅拷贝List受影响,原List无变化 |
|
实现复杂度 |
简单(JDK自带方法直接用) |
复杂(需元素类支持序列化/克隆) |
|
内存开销 |
小(仅拷贝引用) |
大(拷贝所有元素对象) |
|
适用场景 |
只读场景、元素无需修改 |
需修改拷贝后元素,且不影响原数据 |
总结
- 核心本质:浅拷贝只复制List容器和元素的引用地址,深拷贝会复制容器+所有元素的真实对象;
- 关键前提:只有List中的元素是引用类型时,深浅拷贝才有差异(基本类型List拷贝无区别);
- 使用选择:只读场景用浅拷贝(高效),需修改拷贝后数据且不影响原数据时用深拷贝(安全)。
通过以上由浅入深的讲解和源码示例,你可以清晰区分List深浅拷贝的差异,以及在实际开发中该如何选择合适的拷贝方式。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...