aspose file tools*
The moose likes Programmer Certification (SCJP/OCPJP) and the fly likes Question regarding Map interface Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Certification » Programmer Certification (SCJP/OCPJP)
Bookmark "Question regarding Map interface" Watch "Question regarding Map interface" New topic
Author

Question regarding Map interface

Guangcheng Zhou
Greenhorn

Joined: Oct 13, 2006
Posts: 11
Hi all,

I was wondering when I pass a reference object as a key to the put() method from Map interface, such as
Map<Object, Object> m = new HashMap<Object, Object>();
MyObject o = new MyObject();
m.put(o, "Some info");

does the put() method make a new copy of MyObject o in the HashMap, or will the key from HashMap refer to the same object on the heap memory?

I also wrote some tests to try to uncover the answer to this question. I found that whenever I make a change to MyObject o (through o.update(), for example), the object in the HashMap m changes as well.
So I am thinking this is because they are referring to the same object.

But if that is true, then why is it that
o.setName("New name"); // Change to a new name, this is the
// name used in the overriden equals()
// Right now, assuming key reference from HashMap m and o are
// referring to the same object, then the following line should not
// return null
m.get(o);
And m.get(o) does return null.

Any thoughts or answers?


Thanks,


Mack
Henry Wong
author
Sheriff

Joined: Sep 28, 2004
Posts: 18117
    
  39

There are two questions here. And I don't think I have a good answer for the second, but here goes...

First, the collection classes do not make copies of the data. The map holds references to the original objects keys and values used to put the object in the map. So if you change the keys and values, it will change in the map as well.

Second, you should not change the value of the key in a map. The map uses the hashcode() and equals() method to store the location of the values. If the values of the keys change, which can change the hashcode(), I am *not* sure what will happen -- but that may explain why you can't retrieve the values anymore if you change a key.

Henry


Books: Java Threads, 3rd Edition, Jini in a Nutshell, and Java Gems (contributor)
James Quinton
Ranch Hand

Joined: Oct 02, 2006
Posts: 94
hashCode() and equals() methods have to be overridden in order to get the value stored in the Map.
if hashCode() returns 0 AND equals() returns true, Map considers the two objects are "same", hence it gives you the value you look for.
if you have the following MyObject:

no matter how many MyObject instances you create, they all give you the same "value" in Map because they are considered "same", even though they are refering to different objects.
if you have following:

the above code gives you "i am o1". because o1 and o2 are considered equal.
Guangcheng Zhou
Greenhorn

Joined: Oct 13, 2006
Posts: 11
Yes. But what I am asking is why sometimes when I pass an object to the get() method, the get() method returns null. For example,
Map<Object, Object> map = new HashMap<Object, Object>();
MyObject myObj = new MyObject(); // hashCode() and equals() has already being overriden. These two methods both use name attribute of MyObject.
// Initializing
myObj.setName("some_name");

// Add to HashMap object
map.put(myObj, "some_value");

// Now modify the value of the name attribute
map.name = "new_name";

// Since HashMap has a reference to myObj, then the object it refers to should be updated as well, according to Henry's post
// Then, hashCode() and equals() should work according to the updated object
map.get(myObj); // Should not return null

However, the last line return null. Why would that happen?


Thanks,

Mack
yogesh sood
Ranch Hand

Joined: Aug 31, 2000
Posts: 108
Hi Mack,



As Henry mentioned,

Second, you should not change the value of the key in a map. The map uses the hashcode() and equals() method to store the location of the values. If the values of the keys change, which can change the hashcode(), I am *not* sure what will happen -- but that may explain why you can't retrieve the values anymore if you change a key.


What Henry meant by this is that even if you change the value of key which in turn will change hashcode, it will not change location of hash bucket in which value object was stored.

Let say you created object with key value as "some_name", and put that in hashmap, when you do that internally using equals() and hashcode() method your key and value object get stored in hash bucket whose address is calculated with value of key i.e "some_name" using hashcode() method.

Say hashbucket that was used to store your value object was having address as #XYZ now you changed value of key from "some_name" to "new_name" now the reference to key object in hash map will also going to point updated value of key. So far everything is fine no surprises.


Now, when you invoke get() on map to get back your value object, hashmap will again use value of your key i.e "new_name" and call your overidden hashcode method with this value to get address of Hash Bucket, now this is the point where problem comes, this time address genereated for hash bucket is lets say #DEFF , so in map your key/value object pair is being searched in bucket with address #DEFF instead of #XYZ which does not have any object and you get null.

So whole thing is value of the key object is used to calculate address of bucket in which object is stored in map each time you invoke get() method and put() method. So if you change the value of key object , after using put() method then you will get unexpected results.

I hope it helps ! if it doesnt let me know i will draw some diagrams to get better understanding.


If its green its biology if its stinkks its chemistry if it has numbers it is Maths and if it doesn't work its TECHNOLOGY
James Quinton
Ranch Hand

Joined: Oct 02, 2006
Posts: 94
Originally posted by Guangcheng Zhou:
Yes. But what I am asking is why sometimes when I pass an object to the get() method, the get() method returns null. For example,
Map<Object, Object> map = new HashMap<Object, Object>();
MyObject myObj = new MyObject(); // hashCode() and equals() has already being overriden. These two methods both use name attribute of MyObject.
// Initializing
myObj.setName("some_name");

// Add to HashMap object
map.put(myObj, "some_value");

// Now modify the value of the name attribute
map.name = "new_name";

// Since HashMap has a reference to myObj, then the object it refers to should be updated as well, according to Henry's post
// Then, hashCode() and equals() should work according to the updated object
map.get(myObj); // Should not return null

However, the last line return null. Why would that happen?


Thanks,

Mack


first of all, your code won't compile because generic parameter type doesn't agree with what you passed into Map.
Second, I've answered your question. As long as you change the value of "name" attribute in MyObject class, the object is not considered as a "same" object any more. Because as you said, "name" is used to calculate hashCode() and decide equals() result.
Guangcheng Zhou
Greenhorn

Joined: Oct 13, 2006
Posts: 11
Thanks, yogesh. Your answer really helped!
Manish Agarwal
Ranch Hand

Joined: Jun 22, 2004
Posts: 34
Hi Yogesh,

Could you please explain it with the help of diagrams.

Thanks in advance.


Thanks and Regards,<br />Manish Agarwal<br />SCJP 1.4 (100%)<br />SCWCD 1.4 (98%)<br />SCBCD 1.3 (Preparing)
yogesh sood
Ranch Hand

Joined: Aug 31, 2000
Posts: 108
Please refer code above.



In step-3 new value of key i.e "New name" is used to generate id of the hash bucket in which lookup will be done and it comes out to be #DEFF.
Dan Doyle
Greenhorn

Joined: Oct 02, 2006
Posts: 8
So, I have a question: can you have the hashmap rehash itself? In other words, if you know it is possible the keys may have updated their state, like when the name property is changed, can you ask the hashmap to recalculate the hashes? Or is it possible to create a new hashmap with the old hashmap as an argument? If not, it seems that the best strategy for keys is to have them be immutable. Is this a correct conclusion? The other conclusion I get from this is that the hash is calculated when the object is placed in the map, not dynamically whenever get is used. This makes sense as you wouldn't want a million object map to recalculate itself everytime you wanted to retrieve an object.

Regards,
Dan
yogesh sood
Ranch Hand

Joined: Aug 31, 2000
Posts: 108
Hi Don,

I think no , you cant,

please correct me if someone else knows about it. I was unable to find anything in API docs.



Yes this is correct and this is what is recommended from API, that you should use immutable object as key. Behavior on usage of mutable object as key is not predicatble.

http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html




Yes you are correct, when you put object in map it calculate hashcode using your overidden hashcode() method. Moreover there is detail information on storage and retrival process for maps in K&B book for SCJP. If possible have a look at it.
 
Consider Paul's rocket mass heater.
 
subject: Question regarding Map interface
 
Similar Threads
Regarding Values in HashMap
hashmap with duplicate key.
Generics (Using Maps) KB exam book page 583- 586
Using object as keys in maps
equal() & hashcode()