【问题标题】:Is shallow copy really needed?真的需要浅拷贝吗?
【发布时间】:2015-09-08 04:37:51
【问题描述】:

我目前正在为 Java 开发一个图形库。如您所料,存在一个Vertex 类。该类包含一个类型为VertexData<T> 的对象,它本身可以包含任何东西。
(我知道这可能是多余的,我可以做Vertex<T>,但就问题而言,这并不重要)。
我让VertexData<T> 实现Cloneable 并有一个public VertexData<T> clone() 方法通过对调用Object 进行序列化和反序列化来返回深层副本,与here 描述的非常相似

现在的问题是,既然我有一个深拷贝方法,那么也有一个浅拷贝有意义吗?如果是这样,在什么情况下浅拷贝比深拷贝更受欢迎?

更新:由于大多数答案和 cmets 都以某种方式解释了浅拷贝是什么,我觉得我必须澄清一下。我确实知道什么是浅拷贝,它是如何工作的,一切。我的问题是,既然我正在开发一个,而且我已经创建了一个深拷贝方法,那么也提供一种浅拷贝方法是否有意义?

我还要在这里补充一点,VertexData<T> 类中不包含原始类型。
那么在用于存储图的顶点数据的容器类的上下文中,是否需要浅拷贝?
如果是这样,您能在我正在开发的背景下想一个例子吗?
如果不是为了完整性,我应该添加浅拷贝方法吗?
这是一个好的做法还是没关系?

【问题讨论】:

  • 当您想要复制共享一个大型底层数据结构或一组数据的类时,浅拷贝很有用。例如,如果您有几个“客户端”类都引用了大型数据结构引用,如果您想要复制客户端,深度复制将尝试复制基础数据,这不一定是你想要的。
  • 视情况而定。浅拷贝一般是指只拷贝一个对象的一个​​层次,而深拷贝一般是指在多个层次上拷贝所有东西。
  • 如果您引用不可变对象,通常会创建浅拷贝,但这只是一个示例,而不是规则。

标签: java generics deep-copy


【解决方案1】:

List<Point> 这样的容器类型在某些情况下可用于保存一组 X、Y 坐标对,但在其他情况下可用于识别其他代码使用的一堆可移动点。前一种情况可以细分为List<Point>的所有者也是其中Point实例的唯一所有者并且可以随意修改它们,或者所有者永远不会修改这些实例但可以共享对它们的引用的子情况使用承诺不会修改它们的代码。

如果 List<Point> 用于封装 (X,Y) 坐标对,但所有者可能会修改其中保存的 Point 对象,则 List<Point> 的正确克隆必须包含对Point 有问题的对象。如果它封装了坐标对,但没有人会修改其中的对象(并且克隆列表的接收者不会将其中对象的引用暴露给任何可能修改它们的代码),那么List<Point> 的正确克隆可以保存引用原始Point 对象或其副本;前者会更快,但后者在语义上仍然是正确的。

如果List<Point> 用于识别 Point 可能被其他代码修改的实例,并且任何此类修改都需要反映在List<Point> 本身中,那么正确的克隆 必须包含对与原始列表相同的Point 对象的引用。如果克隆要保存那些Point 对象的副本,那么它将不再保存与原始列表相同的语义信息。

如果Java 已经根据它们是使用专有的可变实例还是可共享的不可变实例来封装值,或者它们是否用于识别其中的事物来分离集合类型,那么就有可能有一个单一的“克隆”概念”,而不是需要“深”和“浅”的克隆。但是,如果没有集合类型之间的这种区别,就必须有克隆方法,这些方法可以根据集合中的内容执行所需的任何操作。

【讨论】:

    【解决方案2】:

    这真的归结为需求。知道您的对象不仅仅具有原始字段,它应该(并且值得庆幸的是)有一个deep copy。是否使用shallowdeep 没有“硬性规定”。由于它是“基于要求”,因此正如@RyanJ 在对另一个答案的评论中指出的那样,提供两者都是安全的。

    如果您希望浅复制您的集合或对象,并更改一个属性,它将更改引用以及复制的对象。另一方面,如果您希望深度复制并能够更改对象或对象副本的值,并且不让它影响副本和原始副本,那么您只需要深度复制。这一切都归结为需求以及您需要您的对象/系统做什么。我的最终建议是两者都做。

    【讨论】:

      【解决方案3】:

      是的,在少数情况下需要。您可以根据以下几点推断需求。

      如果对象只有原始字段,那么你应该去浅拷贝。

      如果对象引用了其他对象,那么根据需求,应该考虑浅拷贝或深拷贝。

      如果没有修改引用,则不需要进行深度复制。这里你应该去浅拷贝。

      如果引用被修改,则首选深拷贝。

      浅拷贝:

      如果值的元素从其他参考中更改,浅复制可能会导致不良影响。

      深拷贝:

      在深度复制期间,对数组值引用的任何更改都不会导致对数组数据引用的更改。

      你可以参考this link 通过示例了解更多信息。

      【讨论】:

        【解决方案4】:

        您不需要浅拷贝。浅拷贝只会为内存中已经存在的对象分配一个新的引用变量。 '=' 运算符将完成这项工作。更多详情,请浏览此帖 - In Java, what is a shallow copy?

        【讨论】:

        • 嗯。我知道什么是浅拷贝。我的问题更像是这样,因为我正在研究图书馆,如果还有一个浅拷贝也有意义的话。如果深拷贝可用,是否还需要那个东西。
        • 我不同意...是否需要浅拷贝或深拷贝不应该取决于库实现者,而应该取决于库的用户。提供这两种方法将确保您的库在所有情况下都是通用的。这并不能真正回答问题。
        • 不。无论谁在使用您的库,都将使用 '=' 运算符来执行此操作。您不必提供。只有当有人想要一个新的引用变量来保存内存时,才会首选浅拷贝。但是,如果您愿意,可以提供一个名为 public ClassName shallowCopy(){ return new ClassName(); 的新方法。 //more stuff;} 如果你想对浅拷贝做更多的事情,但你不能限制你的库的用户使用'='操作符。
        猜你喜欢
        • 2011-03-22
        • 1970-01-01
        • 2012-04-12
        • 2015-01-13
        • 2011-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多