Peculiar HashMap Behavior - java

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.

Related

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.

Hashtable get() operation not working as per documentation

According to the official documentation for the Java Hashtable class (https://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html), the get() opperation will return one of it's recorded values, if said value has a key that returns true when the parameter is fed into that key's equals() opperation.
So, in theory, the following code should return "Hello!" for both of the Hashtable's get() queries:
public static class Coordinates implements Serializable {
private int ex;
private int why;
public Coordinates(int xCo, int yCo) {
ex = xCo;
why = yCo;
}
public final int x() {
return ex;
}
public final int y() {
return why;
}
public boolean equals(Object o) {
if(o == null) {
return false;
} else if(o instanceof Coordinates) {
Coordinates c = (Coordinates) o;
return this.x() == c.x() && this.y() == c.y();
} else {
return false;
}
}
}
Hashtable<Coordinates, String> testTable = new Hashtable<Coordinates, String>();
Coordinates testKey = new Coordinates(3, 1);
testTable.put(testKey, "Hello!");
testTable.get(testKey); //This will return the "Hello" String as expected.
testTable.get(new Coordinates(3, 1)); //This will only return a null value.
However, get() doesn't work as it's supposed to. It seems to only work if you litterally feed it the exact same object as whatever was the original key.
Is there any way to correct this and get the Hashtable to function the way it's described in the documentation? Do I need to make any adjustments to the custom equals() opperation in the Coordinates class?
To be able to store and retrieve objects from hash-based collections you should implement/oeverride the equals() as well as hashCode() methods of the Object class. In your case, you have overridden the equals() and left the hashCode() method to its default implementation inherited from the Object.
Here is the general contract of the hashCode() method you must consider while implementing it:
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 hash tables.
And here is an example implementation that is generated from my IDE (as alread mentioned by #Peter in the comment area) which you can modify to suit your requirements:
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ex;
result = prime * result + why;
return result;
}

What factors decide if a new element makes it through addition in HashSet?

I want to know what factors (1, 2 or 3?) are checked before adding a new element (object) to the HashSet?
If an unique element (object) pre exists in the HashSet, and we try to add new object, does the set compare only hashcodes or reference using equals() or both?
HashCode()
equals()
Both 1 and 2
In other words,
if hashTable.add(obj1) => returns true,
and
hashTable.add(obj2) => returns false,
What factors were considered to check in obj2 that rejected it from storage in HashSet.
I tried to print logs by overriding function calls, but didn't equals() was never called while adding element to set.
public class HashTest {
int a,b;
public HashTest(int a, int b){
this.a=a;
this.b=b;
}
public static void main(String[]args){
HashSet<HashTest> hashTable=new HashSet<HashTest>();
HashTest obj1=new HashTest(1, 2);
HashTest obj2=new HashTest(1, 2);
System.out.println("1. obj1 hash code:"+obj1.hashCode());
System.out.println("2. obj2 hash code:"+obj2.hashCode());
System.out.println("inserting obj1 to the Hash Table:"+hashTable.add(obj1));
System.out.println("inserting obj2 to the Hash Table:"+hashTable.add(obj2));
}
public boolean equals(Object obj){
System.out.println("***equals called");
return super.equals(obj);
}
public int hashCode(){
System.out.println("***hashCode called");
return super.hashCode();
}
}
Results:
***hashCode called
obj1 hash code:4072869
***hashCode called
obj2 hash code:1671711
***hashCode called
inserting obj1 to the Hash Table:true
***hashCode called
inserting obj2 to the Hash Table:true
A HashSet utilizes a HashMap, as shown in the implementation:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
where map is an instance of HashMap. Here is the implementation of HashMap#put(K, V):
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
#SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)table[i];
for(; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
I think you should be able to answer your own question now.
Both of them have to be considered.
Consider the following example,
If we have a HashSet<String>
we add Amy and May both are String Objects, with same HashCode, but
they aren't equal.
hashCode will determine the bucket the object to be placed into.
equals will determine if they are the same object.
Hope that helps.
Both are used1. This is how a Hash-Set (see Hash table) works2.
hashCode first determines the bucket used and then equals is always used for final equality testing. The rules between hashCode and equals are explain the Object contract documentation.
So, if either the hashCode fails (i.e. finds empty bucket) or, later, the equals fails (i.e. no equivalent item in bucket), then the item is "not present" and can be added. This also implies that hashCode test can "fail fast" but cannot "succeed fast".
1Identity equality (==) may be applied before equivalency (equals) as it does not change the contract semantics. However, equals is used in all collection types (except rare exceptions like IdentityHashMap) as the "ultimate" test.
2There are different kinds of hash-tables; the above assumes chaining, but similar reasoning applies for all hash strategies.

Java - Retrieving objects from HashMap

I have a problem when retrieving values from a hashmap. The hashmap is declared as follows:
HashMap<TRpair,A> aTable = new HashMap<TRpair,A>();
I then put 112 values into the map as follows:
aTable.put(new TRpair(new T(<value>),new Integer(<value>)),new Ai());
where Ai is any one of 4 subclasses that extend A.
I then proceed to check what values are in the map, as follows:
int i = 0;
for (Map.Entry<TRpair,A> entry : aTable.entrySet()) {
System.out.println(entry.getKey().toString() + " " + entry.getValue().toString());
System.out.println(entry.getKey().equals(new TRpair(new T("!"),new Integer(10))));
i++;
}
i holds the value 112 at the end, as one would expect and the equality test prints true for exactly one entry, as expected.
However, when I do
System.out.println(aTable.get(new TRpair(new T("!"), new Integer(10))));
null is output, despite the above code snippet confirming that there is indeed one entry in the map with exactly this key.
If it helps, the class TRpair is declared as follows:
public class TRpair {
private final T t;
private final Integer r;
protected TRpair(Integer r1, T t1) {
terminal = t1;
row = r1;
}
protected TRpair(T t1, Integer r1) {
t = t1;
r = r1;
}
#Override
public boolean equals(Object o) {
TRpair p = (TRpair)o;
return (p.t.equals(t)) && (p.r.equals(r));
}
#Override
public String toString() {
StringBuilder sbldr = new StringBuilder();
sbldr.append("(");
sbldr.append(t.toString());
sbldr.append(",");
sbldr.append(r.toString());
sbldr.append(")");
return sbldr.toString();
}
}
the equals() and toString() methods in each of the Ai (extending A) and in the T class are overridden similarly and appear to behave as expected.
Why is the value output from the hashmap aTable null, when previously it has been confirmed that the value for the corresponding key is indeed in the map?
With many thanks,
Froskoy.
The keys/elements for a Hash collection but override hashCode() if euqals is overridden.
You could use.
public int hashCode() {
return t.hashCode() * 31 ^ r.hashCode();
}
BTW: It appears from your code that Integer r cannot be null in which case using int r makes more sense.
From Object.equals()
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
IIRC hashmap looks up by hashCode() and not by equality, and since you did not implemented hashcode you use default implementation which is consistent with object pointer equality -
you need to implement proper hashcode function which takes into account "T" parameter as well as integer (or not)
It is good practice that hashCode() and equals() are consistent, but not structly necessary if you know what you are doing.

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