retrieval of duplicate keys in hashmap - java

Since duplicate key overrides the previous key and its corresponding value in hashmap. But if i call get() method and provide previous key as an argument, it returns the overriden value. How it is possible since that key is overriden by new key so it should throw an exception.
//This class is used as a key
Class MapKey
{
public boolean equlas( Object o)
{
return true;
}
}
//This is test class
class MapTest
{
public static void main(String a[])
{
Map m=new Hashmap<Mapkey, String>();
MapKey mk1=new MapKey();
m.put(mk1,"one");
MapKey mk2=new MapKey();
m.put(mk2,"two");
MapKey mk3=new MapKey();
m.put(mk3,"three");
System.out.println(m.get(mk1));
System.out.println(m.get(mk2));
}
}
output: three
three
Since keys are equal so it should get overriden by last key object mk3.
so how is it possible to retrieve value with first or second key object?
Thanks in advance

This is because you have not overrided hashcode method. In equals method you have declared all the objects are equal. But due to default hashcode implementation all equal objects are returning different hashcodes. As hashcodes are used to save values in a hashmap, you are able to get values for old keys mk1 and mk2.

The equals method isn't the most important for HashMap. As the name suggests, the most important method for inserting values into the HashMap is hashCode, not equals. A value is only overwriten in HashMap, if a key k in the map fullfills the following condition: k.hashCode() is equal to the hashCode of the key for inserting an item and the key equals k.

The code you post doesn't come up with
three
three
even if I fix it to clean up the compile errors:
import java.util.*;
class MapKey
{
public boolean equlas( Object o)
{
return true;
}
}
class MapTest
{
public static void main(String a[])
{
Map<MapKey, String> m=new HashMap<MapKey, String>();
MapKey mk1=new MapKey();
m.put(mk1,"one");
MapKey mk2=new MapKey();
m.put(mk2,"two");
MapKey mk3=new MapKey();
m.put(mk3,"three");
System.out.println(m.get(mk1));
System.out.println(m.get(mk2));
}
}
This prints
one
two
Even if I fix the MapKey to override equals:
class MapKey
{
public boolean equlas( Object o)
{
return true;
}
public boolean equals( Object o)
{
return true;
}
}
this prints the same output.
If I implement hashCode:
class MapKey
{
public int hashCode()
{
return 1;
}
public boolean equals( Object o)
{
return true;
}
}
then it will print out
three
three
The default implementations of hashCode and equals are based on the object reference, references to different objects will not be equal. Maps use hashCode to decide which bucket to store an object in, and use equals to differentiate between different objects in the same bucket.
Here you made three different instances so their hashCodes will be different (as long as there isn't a collision, in which case the equals method will get called to decide if the two objects are the same). In this case there isn't a collision, and it's not until hashCode gets overridden that the different mapKey instances are treated as equivalent.

Related

Peculiar HashMap Behavior

I was reviewing one of Oracle’s Java Certification Practice Exams when I came across the follow question:
Given:
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
And this code snippet:
Map m = new HashMap();
MyKeys m1 = new MyKeys(1);
MyKeys m2 = new MyKeys(2);
MyKeys m3 = new MyKeys(1);
MyKeys m4 = new MyKeys(new Integer(2));
m.put(m1, "car");
m.put(m2, "boat");
m.put(m3, "plane");
m.put(m4, "bus");
System.out.print(m.size());
What is the result?
A) 2
B) 3
C) 4
D) Compilation fails
My guess was B because m1 and m3 are equal due to their key references being the same. To my surprise, the answer is actually C. Does put() do something that I am missing? Why wouldn’t "plane" replace "car"? Thank you!
With given definition of class i.e
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
It will result ans = 4, it has only equal method, if you add definition of hashcode then it will result ans=3
class MyKeys {
Integer key;
MyKeys(Integer k) {
this.key = k;
}
#Override
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
#Override
public int hashCode(){
return key*key;
}
}
Contract of equal and hashcode:
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. If you only override equals() and not hashCode() your class violates this contract.
The problem you will have is with collections where unicity of elements is calculated according to both .equals() and .hashCode(), for instance keys in a HashMap.
If you have two objects which are .equals(), but have different hash codes, you lose!
If we keep this simple, since this is for a Java Certification.
Notice that MyKeys doesn't override hashCode, you know there will be something about it. And I usually try to remember only one thing about Object.hashCode
As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (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.)
Or in short, every instance will have a distinct hashcode. Meaning that with this code, every new MyKeys will add a new pair in the map.
In reality, this is a bit more complex since the method still return an integer, so the risk of collision is still present (integer doesn't provide infinite amount of values). You can see a bit more about this here.
This explain why the answer is that the map will have a size of 4. Each key inserted is a different instance.
As answered by others, the ans will be 4, reason being not overriding hashcode method.
For a more clear reason, whenever an object is added in hash map, the hashcode of the key is generated, which decides the location of the entry set. The 2 objects m1 and m3 will have different hash codes as the hashcode method is not overridden (usual hashcode behaviour). Different hash code will not create any collision and a new entry is made.
On the contrary, the equals methods is called only after the hashcode method produces the same result, i.e., same hash code.
In case of m2 and m4 also, the 2 objects have different hash codes, hence 2 different entries, with no calling done to the equals method.
Hence, in cases of hashing, it is necessary to overload hashcode method, along with equals.
It will be more clear when we see the implementation of put method of HashMap.
// here hash(key) method is call to calculate hash.
// and in putVal() method use int hash to find right bucket in map for the object.
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
In your code you #Override only equals method.
class MyKeys {
Integer key;
MyKeys(Integer k) {
key = k;
}
public boolean equals(Object o) {
return ((MyKeys) o).key == this.key;
}
}
To achieve output you need to override both hashCode() and equals() method.

Hashcodes are appearing different for two user defined objects of same type

I have a program to search the key to print the values from a hashmap. But my inputs to the Key and Values are objects that are user defined.Now when I'm equating input key with key1 why are the hashcodes of the Objects key and key1 in the program appearing different, although the return type is same,ie. NameInit, where the hashcodes of the String str="abc" and abc are returned equal? How to check the equality of key and key1 in the program? I tried Objects.equals(key,key1) after type-casting to Object class, but still did not work.I have seen questions of similar kind like in [this question][1] that discusses about the hashcode equality, but then again how to do the equality of these objects as in my example. Kindly help.
NameInit Class
public class NameInit {
String name;
public NameInit(String name)
{
this.name = name;
}
#Override
public String toString(){
return name;
}
}
PlaceAndAddInit
public class PlaceAndAddInit {
String place;
int value;
public PlaceAndAddInit(String place,int val) {
this.place = place;
this.value= val;
}
#Override
public String toString(){
return place+" "+value;
}
}
Main Class
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
HashMap h = new HashMap();
System.out.println("Limit: ");
int limit = scan.nextInt();
for(int i=0;i<limit;i++)
{
h.put(new NameInit(scan.next()), new PlaceAndAddInit(scan.next(),
scan.nextInt()));
}
System.out.println("Enter a key to search the value: ");//
NameInit key= new NameInit(scan.next());//asks for a input from the user to fetch the values
Set s = h.entrySet();
Iterator<NameInit> itr = s.iterator();
while(itr.hasNext())
{
Map.Entry<NameInit,PlaceAndAddInit> me = (Map.Entry) itr.next();
NameInit key1 =me.getKey();
if(key.equals(key1)){// this never happens with this code as key and key1 holds different hashcodes. So how do I achieve the equality.
System.out.println(me.getValue());
}
}
}
}
Edit: I tried to obtain equality by equals method to which I discovered that hashcodes of key1 and key are different. Understanding the reason behind this is the purpose of my question.
You're not overriding hashCode() so the default is used. In the default implementation, key and key1 will have different hashCode values and they will not be equal even if you think they should be. So the solution is to override the hashCode and equals method if you want to be able to compare those objects.
To answer your question:
Edit: I tried to obtain equality by equals method to which I
discovered that hashcodes of key1 and key are different. Understanding
the reason behind this is the purpose of my question.
If you don't provide overrides to equals and hashCode, they will get inherited from Object. And here's how they look for Object:
public boolean equals(Object obj) {
return (this == obj);
}
Therefore, 2 objects will only be equal when they are == which means that they point to precisely the same memory location. In your example, it is not the case. hashCode is native so can't show you the source code.
Here's more to read:
Google search about hashCode and equals
Objects.equals is implemented like this:
return (a == b) || (a != null && a.equals(b));
You see, it is basically calling a's equals method, not the hashcode method. No matter how hashcode is implemented Objects.equals returns false when a.equals(b) returns false. It has nothing to do with the hashcode method.
So to fix this, simply override the equals method. This is a simple implementation:
#Override
public boolean equals(Object obj) {
return this.hashcode() == obj.hashcode();
}
Also, if you want to find the value of a key in the hash map, call the get method on the hash map and it will do it for you in O(1) time. No need for such an inefficient approach with O(n) time.

Containment issues with HashTable class

I have an issue with the Hashtable class. I have a class TmpClass implementing a method equals. Then I create a Hashtable, and two objects of TmpClass being equal under my predifined equals method. Then I put of the object as a key in the Hashtable.
But when I test if the second object is actually contained in the Hashtable, the result is "false"...
Here is my main method.
public static void main(String[] args){
Hashtable<TmpClass, Integer> list = new Hashtable<TmpClass, Integer>();
TmpClass v1 = new TmpClass(1);
list.put(v1, 1);
TmpClass v2 = new TmpClass(1);
if(v2.equals(v1))
System.out.println("Equals");
else System.out.println("Not equal");
if(list.containsKey(v2))
System.out.println("Contains");
else System.out.println("Not contain");
}
Here is my TmpClass.
public class TmpClass {
private int val;
public TmpClass(int v){
val = v;
}
public boolean equals(Object o){
if(o instanceof TmpClass){
return val == ((TmpClass) o).val;
}
else return false;
}
}
It's clearly written in the javadoc that the method containsKey of Hashtable uses the method equals of the Object class to compare the keys. Does somebody have a explanation why then the inheritance property is not satisfied here? Or does somebody have an alternative way to solve this problem?
It would be very helpful for me. Thanks.
Implementing equals is not enough.
In order to use your class as a key in a Map you must also implement hashCode.
here is an example:
#Override
public int hashCode() {
return Integer.valueOf(val).hashCode();
}
You must also implement the hashcode method, as part of the hashcode equals contract. It's also stated in the Hastable spec:
To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.

HashSet behavior when changing field value

I just did the following code:
import java.util.HashSet;
import java.util.Set;
public class MyClass {
private static class MyObject {
private int field;
public int getField() {
return field;
}
public void setField(int aField) {
field = aField;
}
#Override
public boolean equals(Object other) {
boolean result = false;
if (other != null && other instanceof MyObject) {
MyObject that = (MyObject) other;
result = (this.getField() == that.getField());
}
return result;
}
#Override
public int hashCode() {
return field;
}
}
public static void main(String[] args) {
Set<MyObject> mySet = new HashSet<MyObject>();
MyObject object = new MyObject();
object.setField(3);
mySet.add(object);
object.setField(5);
System.out.println(mySet.contains(object));
MyObject firstElement = mySet.iterator().next();
System.out.println("The set object: " + firstElement + " the object itself: " + object);
}
}
It prints:
false
The set object: MyClass$MyObject#5 the object itself: MyClass$MyObject#5
Basically meaning that the object is not considered to be in the set, whiile its instance itself apparantly is in the set. this means that if I insert a object in a set, then change the value of a field that participates in the calculation of the hashCode method, then the HashSet method will seize working as expected. Isn;t this too big source of possible errors? How can someone defend against such cases?
Below is the quote from Set API. It explains everything.
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.
http://docs.oracle.com/javase/7/docs/api/java/util/Set.html
HashSet is implemented on HashMap.
HashMap caches the hashCode of the key, So if you change the hashCode than even though the hash function maps the hashCode to the same bucket as the original object present but it will not find because before even checking the object equality it will check the hashCode.
see the line:
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
And if the hashCode maps to different bucket by hash function than the original object is present than obviously it can't find.
So even though same object if you change the hashCode hashSet can't find. Hope it helps.
So key for the HashMap or the object that you are putting into HashSet should be immutable or effective immutable.
#fazomisiek
public HashSet() {
map = new HashMap<E,Object>();
}
Similarly if you check the source of HashSet you can find it.
This problem is just a limitation of the implementation of java.util.HashSet and the underlying java.util.HashMap. Fundamentally, you're trading off the ability to modify elements in the set for faster insert/lookup performance - it's just part of the contract of using a hash set / map data structure.
If you can't guarantee everybody will remember they can't modify the objects in the set, the only way to absolutely guard against this happening is to only insert immutable objects into the set in the first place.

Java collections - overriding equals and hashCode

class Hash {
int a;
Hash(int h){
a=h;
}
public boolean equals(Object o) {
Boolean h=super.equals(o);
System.out.println("Inside equals ");
return h;
}
public int hashCode() {
System.out.println("Inside Hash");
return 2;
}
}
public class Eq {
public static void main(String...r) {
HashMap<Hash,Integer> map=new HashMap<Hash,Integer>();
Hash j=new Hash(2);
map.put(j,1);
map.put(j,2);
System.out.println(map.size());
}
}
output was
inside hash
inside hash
1
Since it returns the same hashcode , the second time an object is added in hashmap it must use the equals method but it doesnt call . So wats the problem here?
The HashMap is testing with == before .equals, and since you are putting the same object twice, the first test passes. Try with:
Hash j=new Hash(2);
Hash k=new Hash(2);
map.put(j,1);
map.put(k,2);
The equality check is done by HashMap in three steps:
hash code is different => unequal
objects are identical (==) => equal
equal method gives true => equal
The second step prevents calling equals since identical objects are always assumed equal.
From the doc
put(): Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced.

Categories