How would I go about implementing a deep copy for Foo
? It contains an instance of Bar
, which then has a reference to that Foo
.
The easiest way to implement a deep copy that might involve circular references, if you want it to be tolerant of changes to the structure later, would be to use an IdentityHashMap
and an IdentityHashSet
(from here). When you want to copy:
IdentityHashMap<Object,Object>
, to map source objects to their clones.IdentityHashSet<Object>
to track all the objects that are currently in the process of being cloned, but haven't yet finished.IdentityHashMap
to see if you've already cloned that bit. If you have, return the copy that you find in the IdentityHashMap
.IdentityHashSet
to see if you're in the middle of cloning the object you've now reached (because of a circular reference). If you have, just set it to null
for now, and move on.IdentityHashSet
, recursively deep clone it, and then when you've finished the recursive call, add the source/clone pair to the IdentityHashMap
, and remove it from the IdentityHashSet
.null
references you left hanging because you encountered a circular reference. You can walk the graph of source and destination simultaneously. Whenever you find an object in the source graph, look it up in your IdentityHashMap
, and find out what it should map to. If it exists in the IdentityHashMap
, and if it's currently null
in the destination graph, then you can set the destination reference to the clone you find in the IdentityHashMap
.This will make sure you don't clone the same part of the graph twice, but always end up with the same reference whenever there's an object that appears twice in your graph. It will also mean that circular references don't cause infinite recursion.
The point of using the Identity
versions is that if two objects in your graph are the same as determined by .equals()
, but different instances as determined by ==
, then a HashSet
and HashMap
would identify the two, and you'd end up joining things together that shouldn't be joined. The Identity
versions will treat two instances as the same only if they're identical, i.e., the same as determined by ==
.
If you want to do all this but without having to implement it yourself, you could have a look at the Java Deep Cloning Library.