Java中的集合及深拷贝与浅拷贝

2023-11-01

Java中的集合及深拷贝与浅拷贝

Java是一种面向对象的编程语言,其中集合是常用的数据结构之一,具有方便快捷的特点。在Java开发中,我们常常需要对集合进行复制(拷贝)操作。但是,拷贝操作并不是简单的复制,而应该分为浅拷贝和深拷贝两种不同的方式,本文将分别介绍Java中集合的浅拷贝和深拷贝实现方法。

集合简介

Java中的集合是一种用于存储对象的容器,其中包括List、Set、Map等多种类型。集合的特点是可以动态地增加、删除和修改其中的元素。Java中的集合框架分为两个接口:Collection和Map,其中Collection是所有集合接口的根接口,其常用的子接口包括List、Set和Queue。Map接口表示键-值映射,常用的实现类有HashMap和TreeMap。

在这里插入图片描述

浅拷贝

浅拷贝是指拷贝对象时,只复制对象的引用而不是对象本身。这意味着拷贝后的对象与原始对象共享同一个内存地址,对于引用类型的成员变量,拷贝后的对象和原始对象都指向同一个实例。

在Java中,可以通过调用Object类的clone()方法实现浅拷贝。该方法会复制对象的字段,但是对于引用类型的字段,仅复制其引用,而不会复制引用指向的对象。

下面是一个示例代码,演示了如何使用clone()方法实现集合的浅拷贝。

import java.util.ArrayList;
import java.util.List;

class Person implements Cloneable {
    private String name;
    private int age;
    private List<String> hobbies;

    public Person(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class ShallowCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        List<Person> list1 = new ArrayList<>();
        List<String> hobbies = new ArrayList<>();
        hobbies.add("reading");
        hobbies.add("swimming");
        Person person1 = new Person("Tom", 20, hobbies);
        list1.add(person1);
        List<Person> list2 = (List<Person>) ((ArrayList<Person>) list1).clone();
        System.out.println(list1 == list2);
        System.out.println(list1.get(0) == list2.get(0));
        System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies());
    }
}

在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过调用list1的clone()方法创建了一个新的集合list2,然后比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是浅拷贝,list1和list2、person1和list2中的Person对象及其hobbies都是共享同一块内存空间的。

深拷贝

与浅拷贝不同,深拷贝是指拷贝对象时,不仅复制对象的引用,而且复制引用指向的对象,即创建一个全新的对象,并将原始对象中的所有字段值复制到新对象中。这样做的好处是,新对象和原始对象之间不存在任何关联,对新对象的修改不会影响原始对象。

在Java中,实现深拷贝的方法有多种,下面介绍两种常用的方式。

1. 序列化与反序列化

Java中的序列化可以将对象转换为字节流,而反序列化则可以将字节流还原为对象。利用这一特性,我们可以通过将对象序列化为字节流,然后再反序列化成新对象的方式实现深拷贝。

下面是一个示例代码,演示了如何使用序列化和反序列化实现集合的深拷贝。

import java.io.*;
import java.util.ArrayList;
import java.util.List;

class Person implements Serializable {
    private String name;
    private int age;
    private List<String> hobbies;

    public Person(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }
}

public class DeepCopyDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        List<Person> list1 = new ArrayList<>();
        List<String> hobbies = new ArrayList<>();
        hobbies.add("reading");
        hobbies.add("swimming");
        Person person1 = new Person("Tom", 20, hobbies);
        list1.add(person1);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(list1);
        oos.flush();
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        List<Person> list2 = (List<Person>) ois.readObject();
        System.out.println(list1 == list2);
        System.out.println(list1.get(0) == list2.get(0));
        System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies());
    }
}

在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过将list1序列化为字节流,然后再将字节流反序列化为新的List对象list2,实现了深拷贝。最后,我们比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是深拷贝,list1和list2、person1和list2中的Person对象及其hobbies均不是共享同一块内存空间的。

2. 手动递归拷贝

手动递归拷贝是另一种实现深拷贝的方式,其思路是先创建一个空的新对象,然后递归地复制原始对象中的所有字段值到新对象中。对于引用类型的字段,需要继续递归拷贝其指向的对象,直到所有字段值均被复制到新对象中。

下面是一个示例代码,演示了如何使用手动递归拷贝实现集合的深拷贝。

import java.util.ArrayList;
import java.util.List;

class Person {
    private String name;
    private int age;
    private List<String> hobbies;

    public Person(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    public String getName() {
        return name;
    }

    publicvoid setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Person deepCopy() {
        List<String> newHobbies = new ArrayList<>();
        for (String hobby : hobbies) {
            newHobbies.add(hobby);
        }
        return new Person(name, age, newHobbies);
    }
}

public class DeepCopyDemo2 {
    public static void main(String[] args) {
        List<Person> list1 = new ArrayList<>();
        List<String> hobbies = new ArrayList<>();
        hobbies.add("reading");
        hobbies.add("swimming");
        Person person1 = new Person("Tom", 20, hobbies);
        list1.add(person1);
        List<Person> list2 = new ArrayList<>();
        for (Person person : list1) {
            list2.add(person.deepCopy());
        }
        System.out.println(list1 == list2);
        System.out.println(list1.get(0) == list2.get(0));
        System.out.println(list1.get(0).getHobbies() == list2.get(0).getHobbies());
    }
}

在上面的示例代码中,我们创建了一个Person类,其中包含一个List类型的成员变量hobbies。然后,我们创建了一个List类型的集合list1,并向其中添加一个Person对象person1。接着,我们通过手动递归拷贝的方式,创建了一个新的集合list2,并将list1中的所有Person对象递归拷贝到list2中。最后,我们比较list1和list2、person1和list2中的Person对象以及person1和list2中的Person对象的hobbies是否相同。可以看到,由于是深拷贝,list1和list2、person1和list2中的Person对象及其hobbies均不是共享同一块内存空间的。

总结

在Java中,集合是一种常用的数据结构,而集合的拷贝操作又分为浅拷贝和深拷贝两种方式。浅拷贝只复制对象的引用,而深拷贝则复制引用指向的对象,创建一个全新的对象。实现浅拷贝可以通过调用Object类的clone()方法或者手动复制对象的字段实现,而实现深拷贝可以通过序列化与反序列化或者手动递归拷贝实现。在实际开发中,我们应该根据具体情况选择合适的拷贝方式,以保证代码的正确性和性能。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java中的集合及深拷贝与浅拷贝 的相关文章

随机推荐

  • kali linux sudo命令注意

    sudo 的使用 举例 软件更新命令 在root用户下 直接 apt update 若在非root用户下 需要输入 sudo apt update
  • 「技术分享」腾讯疯狂扩招中,只有不断Up知识才有可能拿高薪

    今年的秋招基本已经进入大规模的开奖季了 很多小伙伴收获不错 拿到了心仪的offer 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的面试题和八股文 为此咱这里也统一做一次大整理和大归类 这也算是划重点了 俗话说得好 他山之石 可以攻玉 多
  • springmvc源码学习(二十)对响应数据进行压缩原理

    目录 前言 一 配置 二 原理 1 参数的注入 2 CompressionHttpHandlerFactory的创建 3 压缩的预言 总结 前言 为了节省带宽 在响应数据比较大的情况下 可以对响应数据进行压缩 返回给前端页面压缩数据 一 配
  • 获取浏览器窗口(window)的宽高

    以下三种方法能够确定浏览器窗口的尺寸 浏览器的视口 不包括工具栏和滚动条 1 对于Internet Explorer Chrome Firefox Opera Safari 浏览器窗口的内部高度 window innerHeight 浏览器
  • MATLAB中删除矩阵或向量中Nan数据

    将A中NaN值去掉 B A isnan A 参考博客
  • 微前端qiankun使用+踩坑

    背景 项目使用qiankun 改造的背景 项目A 项目B 项目C 项目A和项目B具有清晰的服务边界 从服务类型的角度能够分为两个项目 在公司项目一体化的背景下 所有的项目又应该是一个项目 项目B研发启动的时候 1 由于开发时间紧张 2 项目
  • Android项目工程结构介绍

    Android项目工程结构介绍 1 gradle和 idea Android Studio自动生成的文件 打包的时候一般会删掉再进行打包 2 app 项目的代码资源都在其中 也是我们工作的核心目录 build 编译生成文件 生成的apk就在
  • Scroller与computeScroll处理滑动

    背景 最近在纯手写一个 slidingMenu 里面用到了 Scroller与computeScroll处理滑动 由于我也是第一次遇到这种东西 我这暴脾气 实在忍不住要记住一下 以供大家参考 更重要的是方便自己以后回忆 知识点讲解 实现滚动
  • 01_08_桶排序(Bucket Sort)

    桶排序 Bucket Sort 桶排序 Bucket Sort 介绍 是一种排序算法 适用于数据范围较小且分布均匀的浮点数数据 它将待排序序列划分为若干个桶 区间 对每个桶中的元素进行排序 然后按顺序合并所有桶的元素得到最终有序序列 桶排序
  • RFID 复杂事件检测算法-毕业论文

    摘 要 本论文首先介绍了RFID技术的概念 工作原理 发展过程 应用背景等信息 然后对本系统所需的硬件条件 即RFID阅读器的特性和配置等信息进行说明 接下来介绍了基于RFID的仓储管理系统的开发背景 探讨了数据库的功能特点 做出了系统需求
  • shell grep 详解说明,实战造就英雄,苦练成就神话

    shell grep 详解说明 当您使用Shell中的grep命令时 它允许您在文本文件或标准输入中搜索匹配某个模式的行 并输出结果 下面是grep命令的详细说明和参数介绍表格 参数 描述 i 忽略大小写进行匹配 默认情况下 grep区分大
  • 制作cmd小游戏_小伙利用Python自制一个推箱子小游戏!

    导语 月初更波python制作小游戏系列吧用python写了个推箱子小游戏 在这里分享给大家 让我们愉快地开始吧 小伙利用Python自制一个推箱子小游戏 开发工具 Python版本 3 6 4 相关模块 pygame模块 以及一些Pyth
  • [转]QT中窗口刷新事件的学习总结

    QT中窗口刷新事件的学习总结 一 主要理解一下几个方法和属性 1 QWidget QScrollView viewport const 2 void QWidget paintEvent QPaintEvent 虚 保护 3 void QW
  • Hive 窗口函数大全

    目录 窗口函数概述 窗口序列函数 row number dense rank 窗口边界 滑动窗口 lag 获取上一行数据 lead 获取下一行数据 窗口专用计算函数 sum累加函数 max最大值 min最小值 avg平均值 count累计次
  • vue的组件通信方法(9种)

    1 传 在 组件的标签上定义属性 组件通过props来进 接收 可以通过数组或者对象的 式接收 如果 组件没有传递属性 组件可以default来设置默认值 父传子的使用场景 封装列表的时候 把数据传到子组件渲染 2 传 组件通过this e
  • 深度学习(30)—— DeformableDETR(1)

    深度学习 30 DeformableDETR 1 原本想在一篇文章中就把理论和debug的过程都呈现 但是发现内容很多 所以就分开两篇 照常先记录理论学习过程 然后是实践过程 注 大家一定不要看过理论就完事儿了 去扣代码 看人家怎么完成的
  • qt5.6.0 opengl —— 纹理贴图

    对于CUBE这个例子 之前分析了它的框架 至于图怎么弄上去的还怎么细看 现在尝试弄了一下 首先分析它怎么对应的 原本是一张图 怎么分成六面的 于是像高中时代那样开始了计算理解 这样就清楚多了 一张图 划分为6个块 一个面一块 至于归一化 可
  • k8s如何对外公布一个应用程序

    一 Kubernetes Service 服务 概述 事实上 Pod 容器组 有自己的 生命周期 opens new window 当 worker node 节点 故障时 节点上运行的 Pod 容器组 也会消失 然后 Deployment
  • Spring Security快速入门

    Spring Security是一个框架 提供 认证 authentication 授权 authorization 和 保护 以抵御常见的攻击 它对保护命令式和响应式应用程序有一流的支持 是保护基于Spring的应用程序的事实标准 spr
  • Java中的集合及深拷贝与浅拷贝

    Java中的集合及深拷贝与浅拷贝 Java是一种面向对象的编程语言 其中集合是常用的数据结构之一 具有方便快捷的特点 在Java开发中 我们常常需要对集合进行复制 拷贝 操作 但是 拷贝操作并不是简单的复制 而应该分为浅拷贝和深拷贝两种不同