所有分类
  • 所有分类
  • 未分类

Java的List之坑–ArrayList的浅拷贝问题

简介

本文介绍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:新建内部对象

说明

新建对象,将原来列表的每一个对象的属性拷贝进去。

下边BeanUtil的代码见这里

代码

package com.knife.controller;

import com.knife.entity.User;
import com.knife.util.BeanUtil;
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 = BeanUtil.deepCopy(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)]
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录