Hello y'all. I am in the midst of an exciting project, and I've hit a little bump. I'm trying to do a 'deep copy' of a TreeMap<String, State> so that I can work with one (delete, insert, etc...) and preserve the other for later. Instead of having to code up a messy method to do this, I'm wondering if clone() or something else, for that matter, might do the trick.

I was thinking something like this:

public TreeMap<String, State> copyStates(TreeMap<String, State> mapping)
{
	TreeMap<String, State> copiedStates = new TreeMap<String, State>();
		
	copiedStates = (TreeMap<String, State>)mapping.clone();
		
	return copiedStates;
}

Thank you in advance!

Member Avatar for ztini

Disregard my previous, you want a deep clone. TreeMap.clone() only does a shallow, no key/values.

http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html#clone()

When it says this in the clone() documentation:

Returns a shallow copy of this TreeMap instance. (The keys and values themselves are not cloned.)

This is odd. I went ahead and implemented the method as I showed in my previous post. Then I used the copied TreeMap and deleted some things. When I printed out the original and copy, they were different, which is what I want. However, the above statement says that the keys and values are NOT cloned, which leads me to believe that if I delete a key in one TreeMap, the corresponding key in the other TreeMap will also go with it. Or have I misunderstood?

Member Avatar for ztini

I too am confused by the javadoc. Here is what I just simulated:

TreeMap<String, String> map = new TreeMap<String, String>();
		
		map.put("foo", "bar");
		System.out.println("  map: " + map.get("foo"));
		
		@SuppressWarnings("unchecked")
		TreeMap<String, String> clone = (TreeMap<String, String>) map.clone();
		System.out.println("\n  map: " + map.get("foo"));
		System.out.println("clone: " + clone.get("foo"));
		
		map.put("foo", "rawr");
		System.out.println("\nchange map.foo to rawr");
		System.out.println("  map: " + map.get("foo"));
		System.out.println("clone: " + clone.get("foo"));
map: bar

  map: bar
clone: bar

change map.foo to rawr
  map: rawr
clone: bar

This indicates that they do not have references to the same objects after clone(). Even though the docs don't necessarily jive, looks like it will work for what you need.

Thanks for the simulation. I've gotten the same results. Quite an interesting find. Does anybody else who understands the Java Doc have any understanding on what is going on, or is this just a mistake in the doc?

In any case, I'm happy it worked to my advantage!

Member Avatar for ztini

TreeMap.clone() may not have the answers, but Object.clone() does:

By convention, the object returned by this method should be independent of this object (which is being cloned). To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it. Typically, this means copying any mutable objects that comprise the internal "deep structure" of the object being cloned and replacing the references to these objects with references to the copies. If a class contains only primitive fields or references to immutable objects, then it is usually the case that no fields in the object returned by super.clone need to be modified.

So, TreeMap.clone is shallow because it does not modify mutable objects:

See the modified example:

class Bar {
			
			private String val;
			
			Bar(String val) { 
				setVal(val);
			}

			public String getVal() {
				return val;
			}

			public void setVal(String val) {
				this.val = val;
			}
			
			public String toString() {
				return val;
			}
		}
		
		TreeMap<String, Bar> map = new TreeMap<String, Bar>();
		
		Bar bar = new Bar("bar");
		
		map.put("foo", bar);
		System.out.println("  map: " + map.get("foo"));
		
		@SuppressWarnings("unchecked")
		TreeMap<String, Bar> clone = (TreeMap<String, Bar>) map.clone();
		System.out.println("\n  map: " + map.get("foo"));
		System.out.println("clone: " + clone.get("foo"));
		
		bar.setVal("rawr");
		System.out.println("\nchange map.foo to rawr");
		System.out.println("  map: " + map.get("foo"));
		System.out.println("clone: " + clone.get("foo"));
map: bar

  map: bar
clone: bar

change map.foo to rawr
  map: rawr
clone: rawr

If it were a deep clone, the cloned map would be unaffected by the change to the Bar object.

http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#clone()

Here is an excellent article on deep v shallow clone: http://www.informit.com/articles/article.aspx?p=20530

Member Avatar for ztini

To your original question, you need to override the clone method and traverse your tree and make clones of mutable objects.

Or, restrict your tree to use only immutable objects to begin with, then you can simply call TreeMap.clone();

See http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html on creating immutable objects.

Interesting find. Thanks! Seems to make more sense - although my program is working just fine without it (so far!) I can see how the above makes sense - to create your program from the ground up striving to make all things immutable.

`clone` here is working as expected. It's cloning the "TreeMap" which is why "put" in the cloned one doesn't affect the "old" one. It's not cloning the key or value; so basically keys and values are "shared" across the two Map instances. This is only an issue if you are modifying the "state" instances which will modify the "state" object of both old and new maps.

I would personally use Java's equivalent of a copy-constructor rather than cloning stuff i.e. Map<String, State> cloned = new TreeMap<String, State>(original); since it at least doesn't throw type safety warnings (clone returns Object).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.