java 深拷贝实用程序如何保留对象的内部关系?
how does java deep copy utility reserve objects' inner relationships?
我正在使用 apache SerializationUtils
来深度复制对象并发现了一些惊人的东西。例如,对象A
有2个成员B1
,B2
,它们都有相同的成员C
(指的是同一个对象).
深度复制后,A'
被创建,我期待 B1'
有成员 C1'
,B2'
有成员 C2'
。但是 B1'
和 B2'
都有相同的成员 C'
.
貌似深拷贝之后,对象的层级和关系还是保持的。这是如何实施的?
我不知道 Apache 库,但很可能它保留了迄今为止复制的实例映射。如果遇到要复制的实例C
,它首先检查是否已经存在该实例的副本C'
。如果是这样,它会使用现有的副本。如果不是,它会创建 C
的深层副本,给出 C'
并将该副本存储在地图中。
需要考虑的一个要点:我猜想 Apache 的存在性测试基于 ==
运算符而不是 equals()
方法,因为 ==
运算符将给出最干净的结果,最类似于原始引用结构。否则,恰好满足 equals()
测试的两个不同实例 C1
和 C2
最终将作为一个副本 C'
.
让我们将两个引用视为具有 'the same identity' 如果它们指向同一个对象。即,给定:
Object a = ...;
Object b = ...;
那么 a
和 b
是 'identical' 如果成立:a == b
,只有当它们指向同一个对象时才会成立。
注意a.equals(b)
是不同的;可以将其持有的任何两个引用视为 'equal',但可能涉及 2 个对象。简单的例子:
String a = new String("Hello");
String b = new String("Hello");
a == b; // this is false
a.equals(b); // this is true
可以确定 2 个引用是否 相同 而不仅仅是 相等.
一个简单的检查就是我刚刚向您展示的内容:==
,它检查相同而不是相等。
SerializationUnits 中的代码很可能使用 WeakHashMap
,这是一个映射到标识(或多或少,'the pointer')的映射。 WHM 主要是一个内部实现,但请注意,您始终可以通过 System.identityHashCode
获取身份哈希码,其中 return 是同一对象的相同值,即使该对象已发生变异。理论上,a.hashCode()
可以 return 一个不同的值(对于可变对象,它倾向于),但是 System.identityHashCode(a)
对于任何给定实例在 VM 的生命周期内都是相同的值。
plain jane HashMap
使用 a.hashCode()
知道要查看哪个桶,然后 a.equals(b)
扫描是否相等。
A WeakHashMap
使用 System.identityHashCode(a)
知道要查看哪个桶,然后 a == b
扫描是否相等。
有了这些,编写一个保留层次结构和关系的序列化程序就变得微不足道了。
另请注意,如果没有这种机制,可靠的序列化是不可能的。毕竟,想象一下这个结构:
List<Object> list = new ArrayList<Object>();
list.add(list); // ooooh, recursion!
如果没有像 WeakHashMap
这样的工具,任何序列化此构造的尝试都将导致 WhosebugError
,原因很明显。
我正在使用 apache SerializationUtils
来深度复制对象并发现了一些惊人的东西。例如,对象A
有2个成员B1
,B2
,它们都有相同的成员C
(指的是同一个对象).
深度复制后,A'
被创建,我期待 B1'
有成员 C1'
,B2'
有成员 C2'
。但是 B1'
和 B2'
都有相同的成员 C'
.
貌似深拷贝之后,对象的层级和关系还是保持的。这是如何实施的?
我不知道 Apache 库,但很可能它保留了迄今为止复制的实例映射。如果遇到要复制的实例C
,它首先检查是否已经存在该实例的副本C'
。如果是这样,它会使用现有的副本。如果不是,它会创建 C
的深层副本,给出 C'
并将该副本存储在地图中。
需要考虑的一个要点:我猜想 Apache 的存在性测试基于 ==
运算符而不是 equals()
方法,因为 ==
运算符将给出最干净的结果,最类似于原始引用结构。否则,恰好满足 equals()
测试的两个不同实例 C1
和 C2
最终将作为一个副本 C'
.
让我们将两个引用视为具有 'the same identity' 如果它们指向同一个对象。即,给定:
Object a = ...;
Object b = ...;
那么 a
和 b
是 'identical' 如果成立:a == b
,只有当它们指向同一个对象时才会成立。
注意a.equals(b)
是不同的;可以将其持有的任何两个引用视为 'equal',但可能涉及 2 个对象。简单的例子:
String a = new String("Hello");
String b = new String("Hello");
a == b; // this is false
a.equals(b); // this is true
可以确定 2 个引用是否 相同 而不仅仅是 相等.
一个简单的检查就是我刚刚向您展示的内容:==
,它检查相同而不是相等。
SerializationUnits 中的代码很可能使用 WeakHashMap
,这是一个映射到标识(或多或少,'the pointer')的映射。 WHM 主要是一个内部实现,但请注意,您始终可以通过 System.identityHashCode
获取身份哈希码,其中 return 是同一对象的相同值,即使该对象已发生变异。理论上,a.hashCode()
可以 return 一个不同的值(对于可变对象,它倾向于),但是 System.identityHashCode(a)
对于任何给定实例在 VM 的生命周期内都是相同的值。
plain jane HashMap
使用 a.hashCode()
知道要查看哪个桶,然后 a.equals(b)
扫描是否相等。
A WeakHashMap
使用 System.identityHashCode(a)
知道要查看哪个桶,然后 a == b
扫描是否相等。
有了这些,编写一个保留层次结构和关系的序列化程序就变得微不足道了。
另请注意,如果没有这种机制,可靠的序列化是不可能的。毕竟,想象一下这个结构:
List<Object> list = new ArrayList<Object>();
list.add(list); // ooooh, recursion!
如果没有像 WeakHashMap
这样的工具,任何序列化此构造的尝试都将导致 WhosebugError
,原因很明显。