【问题标题】:The copy constructor creates dependent copy复制构造函数创建依赖副本
【发布时间】:2013-07-17 14:49:56
【问题描述】:

我按照here 的描述实现了复制构造函数。但问题仍然是当我更新route_copy 时,同样的更新会应用到route。所以,我不明白我的代码有什么问题?

public class Route implements Comparable<Route> {
    private List<Site> sites;

    public Route()
    {
        sites = new ArrayList<Site>();
    }

    public Route(List<Site> sites)
    {
        this.sites = sites;
    }

    /**
     * Copy constructor
     */
    public Route(Route r) {
        this(r.sites);
    }

    public void deleteSite(Site s) {
        this.sites.remove(s);
    }
}

public processData(Route route)
{
  Route route_copy = new Route(route);
  Site s = selectSite(route_copy);
  route_copy.deleteSite(s); // !!! now 'route' does not contain an  element 's'
}

【问题讨论】:

  • 您的“复制构造函数”没有复制输入列表。尝试类似
  • Java 并不像 C++ 那样真正支持复制构造函数,即自动调用。覆盖clone() 不是更合适吗?
  • @DuncanACoulter:在重写 clone 方法并将类 Route 实现为 Cloneable 后,我遇到了同样的问题。
  • @KlausosKlausos 也有可能,一些搜索显示当前的共识是avoid clone

标签: java clone copy-constructor


【解决方案1】:

在你的拷贝构造函数中,你只是做一个浅拷贝,而你需要做一个深拷贝:

public Route(Route r) {
    this(r.sites);
}

在这里,您仍然在复制list 的引用,它仍然指向相同的ArrayList。您也应该修改它以创建列表的副本。可能,您还需要像这样在 arraylist 中创建元素的副本:

public Route(Route r) {
    List<Site> newSites = new ArrayList<Site>();

    for (Site obj: r.sites) {
        // Add copy of obj to the newSites
        // So you need yet another copy constructor in 'Site' class.
    }

    this.sites = newSites;
}

查看这篇文章 - Shallow Copy vs Deep Copy.

【讨论】:

  • 这不会编译。 this(...) 必须是构造函数中的第一条语句。
  • @damo。哎呀。固定的。谢谢:)
  • ITSPoI 仍然会引用 2 列表中相同的 ITSPoI,如果你改变列表中的对象会被反映,仍然是浅拷贝
  • 如果列表是可序列化的,您也可以对列表进行序列化和反序列化以进行深层复制
  • 我认为这取决于意图。如果Site 不被Route拥有,您就不会希望创建同一个站点的多个实例...
【解决方案2】:

您的“复制构造函数”没有复制输入列表。尝试类似

public Route(List<Site> sites)
{
    this.sites = new ArrayList<Site>(sites);
}

为您的第二个构造函数。

【讨论】:

  • 这会创建一个完全独立的副本吗?
  • 它将创建 ArrayList 的副本,但不会创建列表本身持有的元素(Site 对象)的副本。
  • @KlausosKlausos ...这意味着copyList.remove()不会影响原始列表; copyList.get(i).setSomething("") 会。所以,这取决于你将如何使用副本。
【解决方案3】:

当然,它会创建依赖副本,也称为 Shallow 副本。

您需要一份深度副本。

【讨论】:

    【解决方案4】:

    问题是两个列表仍然指向相同的内存位置,因此,对一个列表的任何操作最终都会修改另一个。

    您可以尝试使用 ArrayList 的复制构造函数:

    公共 ArrayList(集合 c)

    构造一个列表 包含指定集合的​​元素,按它们的顺序排列 由集合的迭代器返回。

    像这样:

         public Route(Route r) {
         this(new ArrayList<Site>(r.sites));
         }
    

    但是请注意,对列表中的 Site 对象进行任何修改可能会对存储在另一个列表中的其他对象产生影响,具体取决于您的 Site 对象的复杂程度。

    【讨论】:

      【解决方案5】:

      您对复制构造函数所做的只是让新的Route 使用旧Route 的列表,因此对其中一个的任何更改都会立即影响另一个

      你需要做的是让复制构造函数创建一个新列表:

      sites = new ArrayList<Site>(oldList);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-06
        • 2019-03-24
        • 2013-12-30
        • 2013-04-29
        • 2011-02-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多