真的需要浅拷贝吗?
Is shallow copy really needed?
我目前正在为 Java 开发图形库。如您所料,存在 Vertex
class。 class 包含类型 VertexData<T>
的对象,并且它本身可以包含任何内容。
(我知道这可能是多余的,我可以只做 Vertex<T>
但为了问题的目的,这并不重要)。
我让 VertexData<T>
实现了 Cloneable
并且有一个 public VertexData<T> clone()
方法通过序列化和反序列化调用 Object
返回一个深拷贝,非常像描述的 [=18] =]
现在的问题是,既然我有一个深拷贝方法,那么也有一个浅拷贝有意义吗?如果是这样,浅拷贝优于深拷贝的情况是什么?
更新: 由于大多数答案和评论都以某种方式解释了什么是浅拷贝,我觉得我必须澄清一下。我确实知道浅拷贝是什么,它是如何工作的,一切。我的问题是,因为它是我正在开发的 库 ,并且由于我确实创建了一个深拷贝方法,所以也提供一个浅拷贝方法是否有意义?
我还要在这里补充一点,VertexData<T>
class.
中没有原始类型
因此,在用于存储图 库 的顶点数据的容器 class 的上下文中,是否需要浅拷贝?
如果是这样,您能在我正在开发的内容中想出一个例子吗?
如果不是为了完整起见,我是否应该添加浅拷贝方法?
这是一个好的做法还是无关紧要?
您不需要浅拷贝。浅拷贝只会为内存中已有的对象分配一个新的引用变量。 '=' 运算符将完成这项工作。更多详情,请看这篇post - In Java, what is a shallow copy?
这真的归结为要求。知道你的对象不仅仅有原始字段,它应该(谢天谢地)有一个 deep copy
。使用shallow
还是deep
没有"hard and fast rule"。由于它是 "based on requirement",正如@RyanJ 在对另一个答案的评论中指出的那样,提供两者是安全的。
如果您希望浅复制您的集合或对象,并更改一个属性,它会同时更改引用和复制的对象。另一方面,如果您希望深拷贝并能够更改对象的值或对象的副本,并且不影响副本和原始副本,那么深拷贝就是您所需要的。这一切都取决于要求以及您需要 object/system 做什么。我的最终建议是两者都做。
像List<Point>
这样的容器类型在某些情况下可用于保存一堆X,Y坐标对,但在其他情况下可用于识别一堆被其他代码使用的可移动点。前一种情况可以细分为 List<Point>
的所有者也是其中 Point
个实例的独占所有者并且可以随意修改它们,或者所有者永远不会修改这些实例但可以与承诺不修改它们的代码共享对它们的引用。
如果List<Point>
用于封装(X,Y)坐标对,但所有者可能会修改其中持有的Point
对象,那么[=10=的适当克隆] 必须包含对相关 Point
对象副本的引用。如果它封装坐标对,但没有人会修改其中的对象(并且克隆列表的接收者不会将对其中对象的引用暴露给任何可能修改它们的代码),那么 List<Point>
的适当克隆可以持有对原始 Point
对象或其副本的引用;前者会更快,但后者在语义上仍然是正确的。
如果 List<Point>
用于 识别 Point
可能被其他代码修改的实例,并且任何此类修改都需要反映在 List<Point>
本身,那么一个正确的克隆 必须 持有对与原始列表相同的 Point
对象的引用。如果克隆要保存这些 Point
对象的副本,那么它将不再保存与原始列表相同的语义信息。
如果Java
根据集合类型是使用独有的可变实例还是可共享的不可变实例来封装值,或者它们是否用于标识其中的内容,那么就可以有一个单一的集合类型"cloning" 的概念,而不是要求 "deep" 和 "shallow" 克隆。然而,如果集合类型之间没有这样的区别,则有必要使用克隆方法,该方法可以根据集合中的内容做任何需要的事情。
是的,在少数情况下是必需的。您可以根据以下几点来推断需求。
如果对象只有原始字段,那么你应该选择浅拷贝。
如果对象有对其他对象的引用,那么根据需求,你应该考虑浅拷贝还是深拷贝。
如果引用未被修改,则不需要进行深拷贝。在这里你应该去浅拷贝。
如果引用被修改,那么深拷贝是首选。
浅拷贝:
如果值的元素从其他引用中更改,浅拷贝可能会导致不良影响。
深拷贝:
在深度复制期间,对数组值的任何更改都不会导致对数组数据的更改。
你可以参考this link
通过示例了解更多信息。
我目前正在为 Java 开发图形库。如您所料,存在 Vertex
class。 class 包含类型 VertexData<T>
的对象,并且它本身可以包含任何内容。
(我知道这可能是多余的,我可以只做 Vertex<T>
但为了问题的目的,这并不重要)。
我让 VertexData<T>
实现了 Cloneable
并且有一个 public VertexData<T> clone()
方法通过序列化和反序列化调用 Object
返回一个深拷贝,非常像描述的 [=18] =]
现在的问题是,既然我有一个深拷贝方法,那么也有一个浅拷贝有意义吗?如果是这样,浅拷贝优于深拷贝的情况是什么?
更新: 由于大多数答案和评论都以某种方式解释了什么是浅拷贝,我觉得我必须澄清一下。我确实知道浅拷贝是什么,它是如何工作的,一切。我的问题是,因为它是我正在开发的 库 ,并且由于我确实创建了一个深拷贝方法,所以也提供一个浅拷贝方法是否有意义?
我还要在这里补充一点,VertexData<T>
class.
中没有原始类型
因此,在用于存储图 库 的顶点数据的容器 class 的上下文中,是否需要浅拷贝?
如果是这样,您能在我正在开发的内容中想出一个例子吗?
如果不是为了完整起见,我是否应该添加浅拷贝方法?
这是一个好的做法还是无关紧要?
您不需要浅拷贝。浅拷贝只会为内存中已有的对象分配一个新的引用变量。 '=' 运算符将完成这项工作。更多详情,请看这篇post - In Java, what is a shallow copy?
这真的归结为要求。知道你的对象不仅仅有原始字段,它应该(谢天谢地)有一个 deep copy
。使用shallow
还是deep
没有"hard and fast rule"。由于它是 "based on requirement",正如@RyanJ 在对另一个答案的评论中指出的那样,提供两者是安全的。
如果您希望浅复制您的集合或对象,并更改一个属性,它会同时更改引用和复制的对象。另一方面,如果您希望深拷贝并能够更改对象的值或对象的副本,并且不影响副本和原始副本,那么深拷贝就是您所需要的。这一切都取决于要求以及您需要 object/system 做什么。我的最终建议是两者都做。
像List<Point>
这样的容器类型在某些情况下可用于保存一堆X,Y坐标对,但在其他情况下可用于识别一堆被其他代码使用的可移动点。前一种情况可以细分为 List<Point>
的所有者也是其中 Point
个实例的独占所有者并且可以随意修改它们,或者所有者永远不会修改这些实例但可以与承诺不修改它们的代码共享对它们的引用。
如果List<Point>
用于封装(X,Y)坐标对,但所有者可能会修改其中持有的Point
对象,那么[=10=的适当克隆] 必须包含对相关 Point
对象副本的引用。如果它封装坐标对,但没有人会修改其中的对象(并且克隆列表的接收者不会将对其中对象的引用暴露给任何可能修改它们的代码),那么 List<Point>
的适当克隆可以持有对原始 Point
对象或其副本的引用;前者会更快,但后者在语义上仍然是正确的。
如果 List<Point>
用于 识别 Point
可能被其他代码修改的实例,并且任何此类修改都需要反映在 List<Point>
本身,那么一个正确的克隆 必须 持有对与原始列表相同的 Point
对象的引用。如果克隆要保存这些 Point
对象的副本,那么它将不再保存与原始列表相同的语义信息。
如果Java
根据集合类型是使用独有的可变实例还是可共享的不可变实例来封装值,或者它们是否用于标识其中的内容,那么就可以有一个单一的集合类型"cloning" 的概念,而不是要求 "deep" 和 "shallow" 克隆。然而,如果集合类型之间没有这样的区别,则有必要使用克隆方法,该方法可以根据集合中的内容做任何需要的事情。
是的,在少数情况下是必需的。您可以根据以下几点来推断需求。
如果对象只有原始字段,那么你应该选择浅拷贝。
如果对象有对其他对象的引用,那么根据需求,你应该考虑浅拷贝还是深拷贝。
如果引用未被修改,则不需要进行深拷贝。在这里你应该去浅拷贝。
如果引用被修改,那么深拷贝是首选。
浅拷贝:
如果值的元素从其他引用中更改,浅拷贝可能会导致不良影响。
深拷贝:
在深度复制期间,对数组值的任何更改都不会导致对数组数据的更改。
你可以参考this link 通过示例了解更多信息。