Java的List之坑系列--ArrayList的浅拷贝问题
原创
©著作权归作者所有:来自51CTO博客作者IT利刃出鞘的原创作品,请联系作者获取转载授权,否则将追究法律责任
简介
本文介绍ArrayList的浅拷贝问题的原因和解决方案。
问个问题:先new ArrayList创建了list1并用add添加对象,再new ArrayList创建了list2,然后list2.addAll(list1)。此时如果list1的数据变了,那么list2的数据是否改变?
答案:会改变,因为addAll是浅拷贝(拷贝引用地址)。new ArrayList(list1)也是同样的结果。
问题复现
代码
controller
package com.knife.controller;
import com.knife.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class HelloController {
@GetMapping("/test")
public String test() {
List<User> userList = new ArrayList<>();
User user1 = new User();
user1.setId(1L);
user1.setUserName("Tony");
User user2 = new User();
user2.setId(2L);
user2.setUserName("Pepper");
userList.add(user1);
userList.add(user2);
System.out.println("旧列表(修改前):" + userList);
List<User> newUserList = new ArrayList<>(userList);
newUserList.get(0).setUserName("AA");
System.out.println("旧列表(修改后):" + userList);
System.out.println("新列表(修改后):" + newUserList);
return "test success";
}
}
entity
package com.knife.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String userName;
}
测试
访问:http://localhost:8080/test
后端结果:(旧list的对象也被改变了)
旧列表(修改前):[User(id=1, userName=Tony), User(id=2, userName=Pepper)]
旧列表(修改后):[User(id=1, userName=AA), User(id=2, userName=Pepper)]
新列表(修改后):[User(id=1, userName=AA), User(id=2, userName=Pepper)]
原因分析
打断点分析
可以发现,旧的list和新的list,里边的user对象都是同一个引用。
代码追踪
代码的调用关系如下所示:
java.util.ArrayList#ArrayList(java.util.Collection<? extends E>)
Arrays.copyOf(elementData, size, Object[].class);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
也就是说:System.arrayCopy是浅拷贝。
解决方案
方案1:用JSON转换
说明
先将旧list转为json,再将json反序列化为新list,再修改新list的对象的值。
下边的JsonUtil代码见这里。
代码
package com.knife.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.knife.entity.User;
import com.knife.util.JsonUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class HelloController {
@GetMapping("/test")
public String test() {
List<User> userList = new ArrayList<>();
User user1 = new User();
user1.setId(1L);
user1.setUserName("Tony");
User user2 = new User();
user2.setId(2L);
user2.setUserName("Pepper");
userList.add(user1);
userList.add(user2);
System.out.println("旧列表(修改前):" + userList);
String json = JsonUtil.toJson(userList);
List<User> newUserList = JsonUtil.toObject(json, new TypeReference<List<User>>(){});
newUserList.get(0).setUserName("AA");
System.out.println("旧列表(修改后):" + userList);
System.out.println("新列表(修改后):" + newUserList);
return "test success";
}
}
测试(新列表改动不影响旧列表)
旧列表(修改前):[User(id=1, userName=Tony), User(id=2, userName=Pepper)]
旧列表(修改后):[User(id=1, userName=Tony), User(id=2, userName=Pepper)]
新列表(修改后):[User(id=1, userName=AA), User(id=2, userName=Pepper)]
方案2:新建内部对象
说明
新建对象,将原来列表的每一个对象的属性拷贝进去。
下边BeanHelper的代码见这里。
代码
package com.knife.controller;
import com.knife.entity.User;
import com.knife.util.BeanHelper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class HelloController {
@GetMapping("/test")
public String test() {
List<User> userList = new ArrayList<>();
User user1 = new User();
user1.setId(1L);
user1.setUserName("Tony");
User user2 = new User();
user2.setId(2L);
user2.setUserName("Pepper");
userList.add(user1);
userList.add(user2);
System.out.println("旧列表(修改前):" + userList);
List<User> newUserList = BeanHelper.convert(userList, User.class);
newUserList.get(0).setUserName("AA");
System.out.println("旧列表(修改后):" + userList);
System.out.println("新列表(修改后):" + newUserList);
return "test success";
}
}
测试(新列表改动不影响旧列表)
旧列表(修改前):[User(id=1, userName=Tony), User(id=2, userName=Pepper)]
旧列表(修改后):[User(id=1, userName=Tony), User(id=2, userName=Pepper)]
新列表(修改后):[User(id=1, userName=AA), User(id=2, userName=Pepper)]