首页 > 编程语言 >Java的List之坑系列--ArrayList的浅拷贝问题

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

时间:2022-11-05 14:03:16浏览次数:43  
标签:userName Java -- ArrayList 列表 userList User import id


简介

        本文介绍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的浅拷贝问题_java

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

标签:userName,Java,--,ArrayList,列表,userList,User,import,id
From: https://blog.51cto.com/knifeedge/5825949

相关文章

  • MyBatis--判断boolean类型实现动态sql--方法/实例
    简介        本文介绍MyBatis如何判断boolean类型实现动态sql。        使用MyBatis时,有时需要使用if标签判断boolean类型,从而决定是否拼接sql(动态查询)。代......
  • Spring Data MongoDB--MongoTemplate查询数据--方法/实例
    简介说明        本文用示例介绍SpringDataMongoDB(MongoTemplate)查询数据的方法。查询方法分类mongoTemplate.find //返回listmongoTemplate.findOne //返回1......
  • Spring Cloud LoadBalancer--自定义负载均衡策略--方法/实例
    简介说明    本文用示例介绍SpringCloudLoadBalancer如何自定义负载均衡策略。        SpringCloudLoadBalancer的默认负载均衡策略为轮询策略。我们可......
  • 数据库事务
    事务是逻辑上的一组操作,要么都执行,要么都不执行。事务的四大特性(ACID)原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用......
  • [Linux]----文件操作(重定向+缓冲区)
    文章目录​​前言​​​​一、重定向​​​​具体操作​​​​dup2​​​​二、关于缓冲区的理解​​​​1.什么是缓冲区​​​​2.为什么要有缓冲区​​​​3.缓冲区在......
  • containerd和shim通信
    containerd-shim-runc-v2启动了一个ttrpcserver(类似grpc)containerd通过ttrpc和containerd-shim-runc-v2 通信来管理容器serviceTask{ rpcState(StateReques......
  • 关于rocketmq 中日志文件路径的配置
    rocketmq中的数据和日志文件默认都是存储在​​user.home​​路径下面的,往往我们都需要修改这些路径到指定文件夹以便管理。服务端日志网上搜索rocketmq日志存储路径的修改......
  • Atomsk孪晶多晶建模方法
    大家好,我是小马老师。本文介绍atomsk孪晶多晶建模方法。atomsk多晶建模的原理是先建立一个晶胞,然后编写一个polycrystal.txt文件,设定最终模型的尺寸和晶粒的个数。atomsk按......
  • 29. 两数相除
    29.两数相除题解:a/b=y等价于b>a-(b*y)>0,只要求出减了多少次b就好了。一个一个地减b,效率太低了,最坏情况下,a=2^31-1,b=1,超时;应该先预......
  • Typora打不开了
    按Windows+R打开运行窗口,输入regedit,点确定,打开注册表,依次展开计算机\HKEY_CURRENT_USER\SOFTWARE\Typora,然后在Typora上右键,点权限,选中Administrtors,把权限全部设置为拒......