谈Java List 拷贝

本文深入探讨了Java中List的拷贝方法,包括浅拷贝和深拷贝的区别,通过实例展示了addAll方法、循环遍历复制及重新创建元素的复制方式,并分析了不同场景下对原始List的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    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的,当然不影响。     

        


    

### 如何在 Android Java拷贝 `List` #### 使用浅拷贝 对于基本类型的列表是不需要深拷贝的对象列表,可以直接利用`ArrayList`的构造函数来进行浅层复制: ```java import java.util.ArrayList; import java.util.List; public class ShallowCopyExample { public static void main(String[] args) { List<String> originalList = new ArrayList<>(); originalList.add("Item1"); originalList.add("Item2"); // 利用构造器进行浅拷贝 List<String> copiedList = new ArrayList<>(originalList); System.out.println(copiedList); } } ``` 这种方法适用于对象之间不存在依赖关系的情况,即原始集合中的元素不会因为修改而影响副本中的对应项。 #### 序列化实现深拷贝 当涉及到复杂的数据结构需要确保完全独立于原数据的新实例时,则应考虑采用序列化的方式来完成深层复制。这里给出一个基于Java内置序列化的例子[^2]: ```java import java.io.*; import java.util.*; class DeepCopyableObject implements Serializable { private String name; // 构造方法、getter/setter省略... } public class DeepCopyExample { @SuppressWarnings("unchecked") public static <T extends Serializable> T deepClone(T obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = null; ObjectInputStream in = null; try { out = new ObjectOutputStream(bos); out.writeObject(obj); // 将对象写入字节流 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); in = new ObjectInputStream(bis); return (T)in.readObject(); // 读取并返回新的克隆体 } finally { if(out != null){ out.close(); } if(in != null){ in.close(); } } } public static void main(String[] args) throws Exception{ List<DeepCopyableObject> listOriginal = Arrays.asList( new DeepCopyableObject(), new DeepCopyableObject() ); // 执行深拷贝操作 List<DeepCopyableObject> clonedList = deepClone(listOriginal); System.out.println(clonedList.equals(listOriginal)); } } ``` 此代码片段展示了如何通过序列化技术创建给定对象的一个全新且独立的副本来达到深拷贝的效果。 #### 高效文件级复制(针对大容量数据) 如果待处理的是大量数据者是较大的二进制资源文件,在某些情况下可能更适合直接使用更高效的API如`Files.copy()` 者其他高级IO工具来代替逐个元素遍历的方式[^4]: ```java Path source = Paths.get("/path/to/source/file"); Path target = Paths.get("/path/to/target/file"); // 复制单个文件至另一位置 Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); ``` 上述三种方案分别适合不同场景下的需求,开发者可以根据具体的应用环境和个人偏好选择最合适的一种方式进行列表其他类型容器的内容复制工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值