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

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'

Related

Can we use instances of Object class as keys in HashMap

The question is simple as title:
Can we use instances of Object class as keys in HashMap?
I am not talking about using any custom class as key, but using Object class instances as a key.
Do we need to take any precautions if at all we can use it?
Yes, you are allowed to use instances of Object as a map's key:
Map<Object, SomeOtherType> myMap = new HashMap<>();
But this may be a dangerous thing to do, as this means that objects of any type can be used as a key object, including objects that are not immutable, and if a key object is used that is an object whose .equals(...) or .hashCode() can later change, expect some bad side-effects with maps that break. This is true for any map where the key is potentially mutable.
Per the Map API:
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.
So in sum: yes, this is possible, but don't do it

Is there thing/things that I should do first before using customized types on hashmap?

I want to use a collection to host my data pairs, the order isn't important, duplicate values are allowed and I need fast operation/indexing, so I chose hashmap.
My question is, do I have to override something first before using it like this?
Hashmap<string, myOwnDataType> mhashmap = new Hashmap ();
For example, in TreeSet, I need to override the comparator in order to use my own customized data type, what preparations should I do in the case of Hashmap???
You're all set. String has its own comparator. But please add <> (to avoid a type safety warning) and use upper and lower case correctly:
HashMap<String, MyOwnDataType> mHashMap = new HashMap<>();
Two types are involved in a map: the type of the keys and the type of the values. To use your own class as the value type you don't have to do anything. This holds for HashMap as well as for TreeMap.
However, to use your own type as the key type in a HashMap you do have to consider the following two things:
A HashMap uses the methods hashCode() and equals() to find key-value pairs and to determine whether two keys are equal. Both methods are implemented in Object, but in most cases you will want to override hashCode() and equals() and make sure they are consistent with each other (that means that when a.equals(b) is true, then a.hashCode() == b.hashCode().
When an object is used as a key in the map, it cannot change anymore (with respect to hashCode() and equals(). Often, keys used in a hash map are immutable.
Strings, which you use in your example, can be used as keys in a HashMap without problems.

HashMap using Object as key

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

Collections break

From this link
Name objects are immutable. All other things being equal, immutable
types are the way to go, especially for objects that will be used as
elements in Sets or as keys in Maps. These collections will break if
you modify their elements or keys while they're in the collection.
How do we know that the class "Name" is immutable? (class name visible in the link above mentioned)
What do they actually mean with "the collections will break if you modify their elements?"
Thanks in advance.
Because with mutable classes, you can change the properties based which they are organized/ordered in the Collections, and the holder class would not know about it.
Think that you could do:
public class Name implements Comparable<Name> {
private String firstname = null
// getters and setters
public int compareTo(Name name) {
// Compare based in firstName
}
}
And then:
Name name1 = new Name("John");
Name name2 = new Name("Mike");
SortedSet<Name> set = new TreeSet<Name>();
set.add(name1);
set.add(name2);
name1.setFirstName("Ralph");
Now, is set ordered or is it not?
In a similar way, changes that affect the hashCode of the instance break HashMap and similars, because the first that does these classes when inserting/retrieving objects is to use a specific bucket based in that value.
What they mean is, lookups based on object will be failed.
For example:
mylist.get(myObject);
will fail because the object reference you have will be different (due to modifications) from the one you are using to do get(...) call.
HashSet and HashMap rely on the contract for equals() and hashCode described in the javadoc for java.lang.Object. That means that for two objects being equal accorrding to equals() the calculated hashCode() must also be equal.
If the hashCode() for a object in a Set or Map changes during the time the object is in the Set or Map the implementation will not find the object as it is saved in the bucket for the old hashCode().
Therefore changing the hashCode() while an object is in a Set or Map is a really bad idea.
From the docs on 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.
From the docks on Set
Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set. A special case of this prohibition is that it is not permissible for a set to contain itself as an element.
Lookups are done with .equals on the keys, if the keys are mutable the lookup will fail.

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.

Categories

Resources