HashMap using Object as key - java

I made a post a few days ago about using a HashMap in a simple banking program, but I'm having issues with using Objects as keys.
HashMap <Account,Client> HM = new HashMap<Account, Client>();
HM.put(new Account(2193,"Uri"), new Client(2193,0,"Uri"));
HM.get(2193,"Uri");
Account and Client are classes in other parts of the source. My issue is that the HM.get isn't working as intended, and is giving me an error. Is there another way I'm to 'get' the value? Not sure how to use the key. Do note, the setup of the HashMap is without error.
Furthermore, is there a better way to go about this?

This will give you better idea. that why you need to override hashcode and equals method.
Why do I need to override the equals and hashCode methods in Java?
After overriding hashcode and equals method.
you need to use your object while getting data from hashMap.
HM.get(new Account(2193,"Uri"));

First of all this code does not compile as you are passing 2 arguments to get() which expects only 1 argument.
That argument is supposed to be the key you use in the map and has to be of the same type you declared while declaring your map, in your case HashMap <Account,Client> HM means that HM (which btw should be lowercase by convention) holds as keys objects of type Account and objects of type Client as values.
It would still compile if you did:
get(2193)
Since get() takes an Object but it would simply return a null.
You need to do get(new Account(2193,"Uri")).
Next you do not need to override equals and hashCode in those classes but it is highly recommended (others already pointed to links saying why). Also as per the doc you should make the keys immutable so they do not change, otherwise you might get strange behavior.
Note: great care must be exercised if mutable objects are used as map
keys. The behavior of a map is not specified if the value of an object
is changed in a manner that affects equals comparisons while the
object is a key in the map. A special case of this prohibition is that
it is not permissible for a map to contain itself as a key. While it
is permissible for a map to contain itself as a value, extreme caution
is advised: the equals and hashCode methods are no longer well defined
on such a map.
For more detailed description of the Map interface follow Oracle's tutorial

Related

Can a StringBuffer be used as a key in a HashMap?

Can a StringBuffer as a key in a HashMap?
If so, what is the difference between using a String and a StringBuffer as the key?
Can a StringBuffer as a key in a HashMap?
No, since StringBuffer overrides neither equals nor hashCode, so it is not suitable as a HashMap key (recall that HashMap relies on those two methods to tell if a given key is present in the map).
Beyond that, StringBuffers are mutable, and you typically want Map keys to be immutable. From Map:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.
No, you cannot, unless you want to distinguish between separate buffers instead of their contents. The StringBuffer class does not implement equals or hashCode which means it inherits these methods from Object. The Object implementation of these methods only distinguishes between object instances, not their contents.
In other words, if you would have two StringBuffer instances with the same contents, they would not be considered equal. Even weirder, if you would reinsert the same buffer with a different value, it would be considered equal to the previous one.
In general you should take care using mutable values as keys. Mutations will not alter the position in the Map, as the Map instance will not be notified of the change. In this case, since equals is not implemented anyway, this issue will not come up.
All classes in java are intended to be used as hash keys, because all of them inherit the supermethod hashCode. Altough there are some cases in which, though it might compile well, would be quite weird, like Connection or Streams... or StringBuffer. This is why:
The main difference between String and StringBuffer is that a String is immutable by design, and it contains a proper implementation of hashCode. StringBuffers, instead, may change, and because of this, this class does not have a proper implementation of hashCode: It does not override the default implementation inherited from Object. Now you can see the consequences: A StringBuffer cannot contain a hash of high quality, nor coherent with its contents, damaging then the result of the hashing algorithm.
Yes, any object can be used as a key in a HashMap, although that may not be a good idea.
Class HashMap
Type Parameters:
K - the type of keys maintained by this map
V - the type of mapped values
From this SO answer:
When you put a key-value pair into the map, the hashmap will look at
the hash code of the key, and store the pair in the bucket of which
the identifier is the hash code of the key. (...) Looking at the above
mechanism, you can also see what requirements are necessary on the
hashCode() and equals() methods of keys (...)
Do notice, however, that StringBuffer does not override the required methods so your "key" will be the object's memory address. From the hashcode() docs:
(This is typically implemented by converting the internal address of
the object into an integer, but this implementation technique is not
required by the JavaTM programming language.)
Meaning it's use as a key will be very different than String's:
Map<String, String> hashA = new HashMap<>();
a.put('a', 'a');
System.out.println(hashA.get('a')); //prints 'a'
Map<StringBuffer, String> hashB = new HashMap<>();
StringBuffer buffer = new StringBuffer('a');
hashB.put(buffer, 'a');
System.out.println(hashB.get(new StringBuffer('a'))); //prints null
System.out.println(hashB.get(buffer)); //prints 'a'

Is it possible to implement a MyHashMap backed by a given HashSet implementation?

As we all known, in Sun(Oracle) JDK, HashSet is implemented backed by a HashMap, to reuse the complicated algorithm and data structure.
But, is it possible to implement a MyHashMap using java.util.HashSet as its back?
If possible, how? If not, why?
Please note that this question is only a discussion of coding skill, not applicable for production scenarios.
Trove bases it's Map on it's Set implementation. However, it has one critical method which is missing from Set which is a get() method.
Without a get(Element) method, HashSet you cannot perform a lookup which is a key function of a Map. (pardon the pun) The only option Set has is a contains which could be hacked to perform a get() but it would not be ideal.
You can have;
a Set where the Entry is a key and a value.
you define entries as being equal when the keys are the same.
you hack the equals() method so when there is a match, that on a "put" the value portion of an entry is updated, and on a "get" the value portion is copied.
Set could have been designed to be extended as Map, but it wasn't and it wouldn't be a good idea to use HashSet or the existing Set implementations to create a Map.

Are there any constraints on the hash map data type

Are there any constraint on the key type in the hash map and hash table?----Interview Question.
I think yes we can customize it as needed.
Technically, no. Generally, you want to use an object that implements equals() and hashCode() although that is not strictly necessary. If you don't, then it will use the base implementations defined by Object which compare object identity. A lot of times, that is not appropriate but sometimes it's fine.
Technically the key doesn't need to be immutable as long as the values used in the equals() and hashCode() implementations are immutable. For example, if your class Foo uses a string "foo" as part of its has then that value "foo" must not change. That's because hash maps put the keys into buckets based on the hashCode() value for efficiency reasons. If the hashCode suddenly changes, the hash map is unaware and the key will now live in the wrong bucket and you'll run into nasty bugs because it's then possible to have "duplicate" objects in your map. Hope that makes sense.
Several things to consider:
Just about the "Type", you cannot use primitive type. This is language constraint of Java. e.g. HashMap<int, Foo> is not valid, you need to use HashMap<Integer, Foo>
Base on the way HashMap work, key should have a meaningful implementation of hashCode() and equals(). How it is "meaningful" depends on your need. It may be possible that the default implementation in Object already serve your need, but you need to aware of it.
Once an object instance is put into the Map as key, its hashCode() and equals() should stay consistent. You should never put to a map, and change the state of the object instance as Key and cause hashCode()/equals() returns different value. The easiest way to ensure it is of course use an immutable object as key. However it is still fine that you use mutable object, but in your code, you ensure changing state of keys are not happening.

Java Hash Map containsKey is returning true when it should not

So I have this method that adds an object to a display. When the display is first opened all of the existing objects get added in to the HashMap 1 by 1. After that the user can add more, 1 at a time which puts a new object into the HashMap.
the Key is Object and the Value is a custom class that contains the Object and a couple of other variables about it.
This all works fine but when the display is closed and a new instance is opened it is supposed to reload all of the objects (including the ones that were created and saved by the user during use) but for some reason for anyone of the newly created ones, it finds an incorrect match when doing containsKey.
I don't really understand why it is finding matches when it should not. When I do containsValue it does not find any incorrect matches (it works as it should) but it doesn't help because when i use HashMap.put(K, V) it overrides the one that it would have returned the false positive for containsKey.
All the code does is iterate through each object, check if the HashMap contains the key already, if it does it returns the value for the key otherwise it creates a value based off the key passed in to the method and puts it into the hashmap.
I'm sorry I can't post code so if you can't help without it I understand but its for work and I'm not sure if it would be okay to post the code, even if its just a snippet.
Any help or guidance would be appreciated, I'm still searching around on google to see if I can find any information.
EDIT: I have found a solution. The hashcode function was returning a value which was essentially an index. The problem was that it got reset after the initial objects so the newly added ones would start from zero overwriting the existing ones. I modified the hashcode and its working.
Thank for the help everyone.
Seems like you have overrided equals method but not the hashCode method in your class.
Note that, if you are using your custom class object as key in your HashMap, make sure that your class overrides both equals and hashCode method, else you would never be able to find the key in the HashMap again.
If you override equals but not hashCode, then your keys might be equal based on your equals method, but its hashCode will be generated from the hashCode method of Object class method, which generates different hashCode for your instances.
In general, if you should either override both equals and hashCode methods, or override none of them. Also, for calculating the hashCodes, you should use only those attributes, that you used for comparison in equals method.
Also, as from one of the comments, you should make sure that your Keys are immutable.
I would suggest that you don't use a Custom Object as a key of your HashMap.
These objects have some identification? Some attribute that doesn't repeat from one object to another? Change the key of the HashMap to this attribute.
This way you can use the .containsKey() passing this attribute as parameter.

Setting key type in HashMap, how?

hi
I want to create a HashMap (java) that stores Expression, a little object i've created.
How do I choose what type of key to use? What's the difference for me between integer and String? I guess i just don't fully understand the idea behind HashMap so i'm not sure what keys to use.
Thanks!
Java HashMap relies on two things:
the hashCode() method, which returns an integer that is generated from the key and used inside the map
the equals(..) method, which should be consistent to the hash calculated, this means that if two keys has the same hashcode than it is desiderable that they are the same element.
The specific requirements, taken from Java API doc are the following:
Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.
If you don't provide any kind of specific implementation, then the memory reference of the object is used as the hashcode. This is usually good in most situations but if you have for example:
Expression e1 = new Expression(2,4,PLUS);
Expression e2 = new Expression(2,4,PLUS);
(I don't actually know what you need to place inside your hashmap so I'm just guessing)
Then, since they are two different object although with same parameters, they will have different hashcodes. This could be or not be a problem for your specific situation.
In case it isn't just use the hasmap without caring about these details, if it is you will need to provide a better way to compute the hashcode and equality of your Expression class.
You could do it in a recursive way (by computing the hashcode as a result of the hashcodes of children) or in a naive way (maybe computing the hashcode over a toString() representation).
Finally, if you are planning to use just simple types as keys (like you said integers or strings) just don't worry, there's no difference. In both cases two different items will have the same hashcode. Some examples:
assert(new String("hello").hashCode() == new String("hello").hashCode());
int x = 123;
assert(new Integer(x).hashCode() == new Integer(123).hashCode());
Mind that the example with strings is not true in general, like I explained you before, it is just because the hashcode method of strings computes the value according to the content of the string itself.
The key is what you use to identify objects. You might have a situation where you want to identify numbers by their name.
Map<String,Integer> numbersByName = new HashMap<String,Integer>();
numbersByName.put("one",Integer.valueOf(1));
numbersByName.put("two",Integer.valueOf(2));
numbersByName.put("three",Integer.valueOf(3));
... etc
Then later you can get them out by doing
Integer three = numbersByName.get("three");
Or you might have a need to go the other way. If you know you're going to have integer values, and want the names, you can map integers to strings
Map<String,Integer> numbersByValue = new HashMap<String,Integer>();
numbersByValue.put(Integer.valueOf(1),"one");
numbersByValue.put(Integer.valueOf(2),"two");
numbersByValue.put(Integer.valueOf(3),"three");
... etc
And get it out
String three = numbersByValue.get(Integer.valueOf(3));
Keys and their associated values are both objects. When you get something from a HashMap, you have to cast it to the actual type of object it represents (we can do this because all objects in Java inherit the Object class). So, if your keys are strings and your values are Integers, you would do something like:
Integer myValue = (Integer)myMap.get("myKey");
However, you can use Java generics to tell the compiler that you're only going to be using Strings and Integers:
HashMap<String,Integer> myMap = new HashMap<String,Integer>();
See http://download.oracle.com/javase/1.4.2/docs/api/java/util/HashMap.html for more details on HashMap.
If you do not want to look up the expressions, why do you want them to store in a map?
But if you want to, then the key is that item you use for lookup.

Categories

Resources