Java 如何复制 List ?

这篇具有很好参考价值的文章主要介绍了Java 如何复制 List ?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  List 复制在项目开发时,使用到的频率还是比较高的。List 复制有浅拷贝和深拷贝两种方式。在陈述复制方法前,先总结下什么是浅拷贝和深拷贝(以下内容均站在 Java 语言基础上进行讨论)。

一、什么是浅拷贝(Shallow Copy)和深拷贝(Deep Copy)?

  浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存。深拷贝会创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对象。
  假设 B 复制了 A,当修改 A 时,看 B 是否会发生变化。如果 B 也跟着变了,说明这是浅拷贝,如果 B 没变,那就是深拷贝。

1.1 浅拷贝

  对于数据类型是基本数据类型(整型:byte、short、int、long;字符型:char;浮点型:float、double;布尔型:boolean)的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。

  对于数据类型是引用数据类型(比如说成员变量是某个数组、某个类的对象等)的成员变量,浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例,在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

1.2 深拷贝

  相对于浅拷贝而言,深拷贝对于引用类型的修改,并不会影响到对应的拷贝对象的值。

  备注:一般在讨论深拷贝和浅拷贝时,通常是针对引用数据类型而言的。因为基本数据类型在进行赋值操作时(也就是拷贝)是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这个时候你修改两者中的任何一个的值都不会影响另一个。而对于引用数据类型来说,在进行浅拷贝时,只是将对象的引用复制了一份,也就是内存地址,即两个不同的变量指向了同一个内存地址,那么改变任一个变量的值,都是改变这个内存地址所存储的值,所以两个变量的值都会改变。
  Java 对对象和基本数据类型的处理是不一样的。在 Java 中,用对象作为入口参数传递时,缺省为 “引用传递”,也就是说仅仅传递了对象的一个”引用”。当方法体对输入变量修改时,实质上就是直接操作这个对象。 除了在函数传值的时候是”引用传递”,在任何用 ”=” 向对象变量赋值的时候都是”引用传递”。
  将对象序列化为字节序列后,再通过反序列化即可完美地实现深拷贝。

1.3 对象如何实现深拷贝?

  Object 对象声明了 clone() 方法,如下代码所示。

	/**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

  Object 的 clone() 方法本身是一个浅拷贝的方法,但是我们可以通过实现 Cloneable 接口,重写该方法来实现深拷贝,除了调用父类中的 clone() 方法得到新的对象, 还要将该类中的引用变量也 clone 出来。如果只是用 Object 中默认的 clone() 方法,是浅拷贝的。
  如下代码所示,我们创建一个测试类 TestClone,实现深拷贝。

package com.example.test;

import lombok.Data;
import lombok.SneakyThrows;

@Data
public class TestClone implements Cloneable {
    private String a;

    // 构造函数
    TestClone(String str) {
        this.a = str;
    }

    @Override
    protected TestClone clone() throws CloneNotSupportedException {
        TestClone newTestClone = (TestClone) super.clone();
        newTestClone.setA(this.a);
        return newTestClone;
    }

    @SneakyThrows
    public static void main(String[] args) {
        TestClone clone1 = new TestClone("a");

        TestClone clone2 = clone1.clone();
        System.out.println(clone2.a);     // a
        clone2.setA("b");

        System.out.println(clone1.a);     // a
        System.out.println(clone2.a);     // b
    }
}

二、List 复制

  List 复制时有深拷贝和浅拷贝两类方式,分述如下。

2.1 浅拷贝

  List 其本质就是数组,而其存储的形式是地址,如下图所示。
Java 如何复制 List ?

  将 listA 列表浅拷贝为 listB 时,listA 与 listB 指向同一地址,造成的后果就是,改变 listB 的同时也会改变 listA,因为改变listB 就是改变 listB 所指向的地址的内容,由于 listA 也指向同一地址,所以 listA 与 listB 一起改变。其常见的实现方式有如下几种(以上述代码中的 TestClone 为测试类)。

2.1.1 循环遍历复制(含测试方法)
	@SneakyThrows
    public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone testClone = new TestClone("a");
        listA.add(testClone);
        System.out.println(listA);  // [TestClone(a=a)]

        List<TestClone> listB = new ArrayList<>(listA.size());
        for (TestClone clone : listA) {
            listB.add(clone);
        }
        System.out.println(listB);  // [TestClone(a=a)]

        listA.get(0).setA("b");
        System.out.println(listA);  // [TestClone(a=b)]
        System.out.println(listB);  // [TestClone(a=b)]
    }
2.1.2 使用 List 实现类的构造方法
	@SneakyThrows
    public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone testClone = new TestClone("a");
        listA.add(testClone);
        List<TestClone> listB = new ArrayList<>(listA);
    }
2.1.3 使用 list.addAll() 方法
	@SneakyThrows
    public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone testClone = new TestClone("a");
        listA.add(testClone);

        List<TestClone> listB = new ArrayList<>();
        listB.addAll(listA);
    }
2.1.4 使用 java.util.Collections.copy() 方法
	public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone clone = new TestClone("a");
        listA.add(clone);

        List<TestClone> listB = new ArrayList<>();
        listB.add(new TestClone("c"));
        System.out.println(listB);      // [TestClone(a=c)]
        Collections.copy(listB, listA);
        System.out.println(listB);      // [TestClone(a=a)]
        listA.get(0).setA("b");
        System.out.println(listA);      // [TestClone(a=b)]
        System.out.println(listB);      // [TestClone(a=b)]
    }
2.1.5 使用 Java 8 Stream API 将 List 复制到另一个 List 中
	public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone clone = new TestClone("a");
        listA.add(clone);

        List<TestClone> listB = listA.stream().collect(Collectors.toList());
        System.out.println(listB); // [TestClone(a=a)]

        listA.get(0).setA("b");
        System.out.println(listA); // [TestClone(a=b)]
        System.out.println(listB); // [TestClone(a=b)]
    }
2.1.6 在 JDK 10 中的使用方式

  JDK 10 在线调试网址:https://lightly.teamcode.com/

	public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone clone = new TestClone("a");
        listA.add(clone);

        List<TestClone> listB = List.copyOf(listA);
    
        listA.get(0).setA("b");
        System.out.println(listA);                  // [TestClone@76ed5528]
        System.out.println(listA.get(0).getA());    // b
        System.out.println(listB);                  // [TestClone@76ed5528]
        System.out.println(listB.get(0).getA());    // b
    }
2.2 深拷贝

Java 如何复制 List ?

  如图,深拷贝就是将 listA 复制给 listB 的同时,给 listB 创建新的地址,再将地址 A 的内容传递到地址 B。listA 与 listB 内容一致,但是由于所指向的地址不同,所以各自改变内容不会影响对方。深拷贝时,向新列表添加的是原列表中的元素执行 clone() 方法后的新对象。

	@SneakyThrows
    public static void main(String[] args) {
        List<TestClone> listA = new ArrayList<>();
        TestClone testClone = new TestClone("a");
        listA.add(testClone);

        List<TestClone> listB = new ArrayList<>();
        listB.add(listA.get(0).clone());
        System.out.println(listB);  // [TestClone(a=a)]

        listA.get(0).setA("b");
        System.out.println(listA);  // [TestClone(a=b)]
        System.out.println(listB);  // [TestClone(a=a)]
    }

  本文提到的几种浅拷贝的方法对于 List<String> 可以实现深拷贝的效果,其测试代码和原因分析见 《Java 如何实现 List<String> 的深拷贝?》。文章来源地址https://www.toymoban.com/news/detail-410022.html

文章参考:
  • 深拷贝和浅拷贝的区别
  • List的深拷贝与浅拷贝

到了这里,关于Java 如何复制 List ?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 元宇宙NFT项目开发底层链如何选择

    自2021年开始,随着元宇宙概念火爆,数字藏品也在国内热起来。阿里、腾讯、京东先后上线数字藏品平台 鲸探、阿里数字拍卖、幻核、灵稀 等都属于联盟链。目前国内头部的数字藏品平台年GMV已过数亿。 首先国内区块链分为联盟链和公链二部阵营,从数量来看国内联盟链占

    2024年02月15日
    浏览(24)
  • 如何使用 Lightly 进行 Python GUI 项目开发

    GUI 即图形用户界面(Graphical User Interface)的缩写,是一种使用图形交互的界面系统。这种系统为软件提供图标、菜单等视觉交互性强的部件,让用户能通过点击、拖动、下拉等方式操作电脑中的软件和应用程序。GUI 所展示的物体可以传递各式各样的信息,同时也会随着用户

    2024年02月05日
    浏览(30)
  • 如何合理使用 Jetpack 组件开发 Android 项目?

    Jetpack 是 Android 官方推出的一套开发库,其中包含众多的组件,可以让 Android 开发者更快更高效地开发应用程序。Jetpack 组件分为四大部分:架构、行为、UI 和基础组件。 下面详细阐述如何合理使用 Jetpack 组件开发 Android 项目。 在使用 Jetpack 组件之前,首先应熟悉几个常用的

    2024年02月02日
    浏览(34)
  • 【java】Java项目从开发到部署生产完整流程梳理

    从事Java开发许久,从最初学习的JDK环境变量开始,到如今开发部署发布,已经逐渐形成了自己的一套体系,当然,其中也不少学习了网上各种资料总结,接下来将在本文对Java项目开发到部署发布整个流程进行归纳梳理。 关于开发环境,在之前写的一篇文章里有详细教学,因

    2024年02月03日
    浏览(31)
  • 【java】【项目实战】[外卖五]菜品管理业务开发

    目录 一、文件上传与下载 1.1 文件上传介绍 1.2 文件下载介绍 1.3 文件上传代码实现 1.3.1 新增upload.html 1.3.2 修改application.yml  1.3.3 CommonController 1.3.4  功能测试 1.4 文件下载代码实现  1.4.1  CommonController 1.4.2  功能测试 二、新增菜品 2.1 需求分析 2.2 数据模型 2.3 代码实现 2.3.

    2024年02月11日
    浏览(31)
  • 【项目实战】Java 开发 Kafka 消费者

    👉 博主介绍 : 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO TOP红人 Java知识图谱点击链接: 体系化学习Java(Java面试专题) 💕💕 感兴趣的同学可以收藏关注下 , 不然下次找不到哟

    2024年02月16日
    浏览(33)
  • 【项目实战】Java 开发 Kafka 生产者

    👉 博主介绍 : 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO TOP红人 Java知识图谱点击链接: 体系化学习Java(Java面试专题) 💕💕 感兴趣的同学可以收藏关注下 , 不然下次找不到哟

    2024年02月16日
    浏览(33)
  • 遇到跨端开发或多项目开发时,遇到的一些问题探讨,后端开发语言如何选择?

    ​最近有同学问我,做后端开发项目时用php,java,c#,go,pathon...哪个好,从最近阿里云、美团服务器崩溃来看,我想给你最直接的回答是,没有完美的,只有适合自己的。咱们讨论最多的问题就是跨多端开发,以及多项目开发后期所带来的升级、维护等相关问题,接下来就

    2024年02月04日
    浏览(34)
  • vue项目,如何区分开发环境、测试环境、正式环境

    在package.json文件中配置 serve和build有默认值。默认值development 和 production 测试环境通常就是开发环境,所以设置为\\\"build:test\\\": “NODE_ENV=development vue-cli-service build”, 这是在MacOS操作系统中,但是在window系统中不起作用 window上会提示 vue如何配置研发/测试/生产环境? 不用配置文件

    2024年01月19日
    浏览(33)
  • 如何应用项目管理软件进行敏捷开发管理

    敏捷开发(Agile Development)是一种软件开发方法论,强调在不断变化的需求和环境下,通过迭代、协作和自适应的方式来开发软件。敏捷方法的目标是提供更快、更灵活、更高质量的软件交付,以满足客户需求并实现项目成功。 在技术研发团队使用敏捷开发来完成一个迭代时

    2024年02月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包