简介

        本文介绍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)]

原因分析

打断点分析

Java的List之坑系列--ArrayList的浅拷贝问题_前端

可以发现,旧的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)]