Java开发人员在开发过程中经常遇到需要对List进行复制的场景,在这些场景中又经常因为没注意浅拷贝和深拷贝的区别而出现异常,下面我们花一点时间,来谈谈Java List的拷贝问题。已经有很多文章列出浅拷贝和深拷贝的各种方式,我们针对使用场景,看看这些拷贝方法。
1. List的元素个数不变(而非元素内容不变)
这种场景主要是当不希望对进行增加/减少元素(不涉及修改已有元素的内容),但不希望影响原始List。
先定义一个User类供使用
public class User {
private String name;
private int age;
User(){}
User(String name, int age){
this.name = name;
this.age = age;
}
}
a) addAll
List<User> list1= new ArrayList<>();
User user1 = new User("alice", 20);
User user2 = new User("bob", 30);
list1.add(user1);
list1.add(user2);
List<User> list2 = new ArrayList<>();
list2.addAll(list1);
[1] 删除
list2.remove(0);
打印:
System.out.println(JSON.toJSONString(list1));
System.out.println(JSON.toJSONString(list2));
输出结果:
list1 = [{"age":20,"name":"alice"},{"age":30,"name":"bob"}]
list2 = [{"age":30,"name":"bob"}]
[2] 增加
然后将删除语句改为如增加语句:
User user3 = new User("Jessica", 25);
list2.add(user3);
输出结果:
list1 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"},{"age":25,"name":"Jessica"}]
可以看到,新List的元素增改没有影响老List。
[3] 修改元素
接下来看下元素内容的修改是影响原始List
list2.get(0).setAge(999);
打印:
System.out.println(JSON.toJSONString(list1));
System.out.println(JSON.toJSONString(list2));
输出结果:
list1 = [{"age":999,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":999,"name":"Alice"},{"age":30,"name":"Bob"}]
我们看到修改其实是对原始List的修改,这点也容易理解,List是new的,所以List的相关不受影响,但元素并不是new的,所以修改的其实是原来的元素。
b) 不重新new元素的 循环遍历复制
for(User user:list1){
list2.add(user);
}
[1] 删除
list2.remove(0);
输出结果:
list1 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":30,"name":"Bob"}]
[2] 增加
然后将删除语句改为如增加语句:
list2.add(new User("Jessica", 25));
输出结果:
list1 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"},{"age":25,"name":"Jessica"}]
可以看到,新List的元素增改没有影响老List。
[3] 修改元素
接下来看下元素内容的修改是影响原始List
list2.get(0).setAge(999);
输出结果:
list1 = [{"age":999,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":999,"name":"Alice"},{"age":30,"name":"Bob"}]
我们看到不重新new元素的循环遍历,效果和addAll是一样的。
c) 重新new元素的 循环遍历复制
for(User user:list1){
User newUser = new User();
newUser.setAge(user.getAge());
newUser.setName(user.getName());
list2.add(newUser);
}
[3] 修改元素
接下来看下元素内容的修改是影响原始List
list2.get(0).setAge(999);
输出结果:
list1 = [{"age":20,"name":"Alice"},{"age":30,"name":"Bob"}]
list2 = [{"age":999,"name":"Alice"},{"age":30,"name":"Bob"}]
不影响原始List,这个可以想到的,List和元素都是new的,当然不影响。