• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Question regarding Map interface

 
Greenhorn
Posts: 11
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Ranch Hand
Posts: 94
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 11
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Ranch Hand
Posts: 108
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
James Quinton
Ranch Hand
Posts: 94
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Posts: 11
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks, yogesh. Your answer really helped!
 
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Yogesh,

Could you please explain it with the help of diagrams.

Thanks in advance.
 
yogesh sood
Ranch Hand
Posts: 108
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 108
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic