【问题标题】:Copy of arraylist keeps getting modified to the values of the originalarraylist 的副本不断修改为原始值
【发布时间】:2013-02-07 02:16:26
【问题描述】:

我正在开发一个用于保存和调用屏幕状态的系统,这是我第一次搞砸这种东西,所以我不确定最好的方法是什么,但我目前存储了所有数组列表中的“PreviewMonitor”对象(大约 40 个左右)。问题是,当我创建一个名为“allPreviewMonitors”的 ArrayList 副本以进行存储时,我最终会得到一个 ArrayList,其中的元素随着原始元素的更新而不断变化。几乎就好像我正在使用原始的 ArrayList,而事实上,它应该是一个完全不同的 ArrayList,当我创建 allPreviewMonitors 的副本时,它具有元素及其状态的“冻结”版本。为什么会发生这种行为?如果需要,我可以显示代码,但我不确定这里是否需要它。

【问题讨论】:

    标签: java arraylist copy duplicates clone


    【解决方案1】:

    一个Arraylist 和所有Collections 一样,只包含对对象的引用。 仅复制 List 是不够的,您还必须在创建 List 副本时 clone() 列表中的元素(或创建新元素,或使用复制构造函数)。

    这称为制作“深拷贝”,而您目前有一个“浅拷贝”。

    【讨论】:

    • 我该如何“克隆” PreviewMonitor 对象。我认为这可能是问题所在,所以我创建了一些东西来测试它,并在 PreviewMonitor 对象中创建了一个“克隆”方法,因为我认为应该创建它,但我不太确定我是否做得对。 public PreviewMonitor clone() { return this; }
    【解决方案2】:

    您只是将对象引用复制到您的 ArrayList 中。您需要自己复制对象。

    在Java中,所有的对象变量实际上都是引用变量。所以代码:

    Myclass myObject = new Myclass();
    Myclass otherObject = myObject;
    

    创建一个 Myclass 对象并将对该 Myclass 对象的引用存储在引用变量myObject 中。然后它创建一个新的引用变量otherObject,并将引用数据(例如内存地址)从myObject 复制到otherObject。这些现在指的是内存中的同一个对象。此时,线

    myObject.myMethod();
    

    结果与

    相同
    otherObject.myMethod();
    

    您在 ArrayList 中得到的是对相同对象的不同引用。 你想要的是以下之一:

    Myclass otherObject = myObject.clone(); // use the clone function
    // OR
    Myclass otherObject = new Myclass(myObject); // use a copy constructor
    

    如果您使用 clone() 或复制构造函数将对象放入 ArrayList,则您的 ArrayList 将包含对相同副本的引用,而不是对相同副本的引用。

    正如其他人所指出的,仅制作引用的副本称为“浅拷贝”,而制作引用的对象的副本称为“深拷贝”。

    编辑:为了使它起作用,您的解决方案不仅要在你的类上实现,而且要在你的类中包含的所有类上实现。例如,考虑MyClass,它的字段类型为OtherClass

    class MyClass {
      private String foo;
      private OtherClass bar;
      private int x;
      MyClass(String f, OtherClass b, int x) {
        foo = f;
        bar = b;
        this.x = x;
      }
    
      MyClass(MyClass o) {
        //make sure to call the copy constructor of OtherClass
        this(new String(o.foo), new OtherClass(o.bar), o.x);
      }
      // getters and setters
    }
    

    请注意,这需要OtherClass 也有一个复制构造函数!如果OtherClass 引用其他类,那么它们也需要复制构造函数。没有办法。

    最后,您的列表副本将如下所示:

    List<MyClass> myNewList = new ArrayList<>(myExistingList.size());
    for ( MyClass item : myExistingList) {
      // use the copy constructor of MyClass, which uses the copy constructor of OtherClass, etc, etc.
      myNewList.add(new MyClass(item))
    }
    

    【讨论】:

    • 我尝试过使用复制构造函数,但是,这导致了相同的输出。可能是因为 PreviewMonitor(我的对象)包含一个 JButton 对象,这就是我使用 PreviewMonitor.getJButton() 拉出的对象吗?
    • 上述方法我也试过了,但是当我修改克隆对象时,它也会反映在原始对象中,不知道出了什么问题。
    • 明确地说,您需要对整个对象进行深层复制,包括其中的所有对象。而且,您必须在您的班级代码中实际实现这一点。您必须提供一个复制构造函数(或克隆方法),它实际上复制了您类中的每个项目。
    【解决方案3】:

    您需要确保执行“深拷贝”——即克隆 PreviewMonitor 对象。默认情况下,您只会做一个浅拷贝并复制对同一对象的引用。

    【讨论】:

    • 我该如何“克隆” PreviewMonitor 对象。我认为这可能是问题所在,所以我创建了一些东西来测试它,并在 PreviewMonitor 对象中创建了一个“克隆”方法,因为我认为应该创建它,但我不太确定我是否做得对。 public PreviewMonitor clone() { return this; }
    • 我假设PreviewMonitor 是您自己的对象之一。克隆过程取决于对象。您通常会调用 clone() 方法。 en.wikipedia.org/wiki/Clone_(Java_method) 有更多信息。
    【解决方案4】:

    要克隆,您不能只返回当前对象。您必须创建一个与当前对象具有相同值的新对象。也就是说,使用当前对象的Class的构造函数,创建一个新对象。确保旧对象和新对象之间的属性匹配。返回新对象并对原始列表中的每个对象重复。

    【讨论】:

    • 我已经为 (PreviewMonitor sourcePreviewMonitor : previewMonitors) { this.previewMonitors.add(new PreviewMonitor(sourcePreviewMonitor.getPos(), sourcePreviewMonitor.getJButton().getX(), sourcePreviewMonitor. getJButton() .getY(), sourcePreviewMonitor.getJButton().getWidth(), sourcePreviewMonitor.getJButton().getHeight(), sourcePreviewMonitor.getColumn(), sourcePreviewMonitor.getRow(), sourcePreviewMonitor.getRight())); } -- 并为 PreviewMonitor 对象创建一个新的构造函数,使其接受一个参数,另一个 PreviewMonitor 并将其传入。
    猜你喜欢
    • 2020-12-30
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    • 2019-01-09
    • 2022-11-12
    • 1970-01-01
    • 2011-09-30
    相关资源
    最近更新 更多