Monday 17 April 2017

Difference between HashMap vs IdentityHashMap in Java


Difference between HashMap vs IdentityHashMap in Java


The IdentityHashMap is one of the lesser known Map implementation from JDK. Unlike general purposes Map implementations like HashMap and LinkedHashMap, it is very special and it's internal working is quite different than HashMap. The main difference between IdentityHashMap and HashMap in Java is that former uses equality operator (==) instead of equals()method to compare keys. Which means you need the same key object to retrieve the value from IdentityHashMap, you cannot retrieve values by using another key which is logically equal to previous key. Another important difference between HashMap and IdentityHashMap is that IdentityHashMap doesn't use hashCode() method instead it uses System.identityHashCode() method. This is a significant difference because now you can use mutable objects as key in Map whose hash code are likely to change when the mapping is stored inside IdentityHashMap. 


Other Map implementation which uses equals() and hashCode() doesn't work well with 
mutable keys. For example, if you store a mapping in HashMap and then went on to change the key object the hashCode generated by key later will not be the same as before. Even if you return same, the equals() method will not return true when you compare key object from entry to given key object.

So, that's the basic difference between IdentityHashMap and a HashMap in Java, let's see a couple of more and some code to understand this concept better.





IdentityHashMap vs HashMap in Java
As I said, IdentityHashMap is a lesser known class from JDK, you might never use this class in your project but a good chance is that someone else might have already used. Since most of the programmers spend more time reading code than writing, it's important to know what  is IdentityHashMap in Java, what it does, how it works, and when to use this class in your Java application.

Once you understand the difference between IdentityHashMap and HashMap, you will automatically learn how to make the best use of this class.

1) The first and foremost difference is that IdentityHashMap internally uses 
== operator instead of equals() method, which means you can store a String object into IdentityHashMap and later call the contains() method to check if it exists in the Map, it will only return true if both objects is same in heap space. It will return false even if both objects has same content, as shown below:


import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;

public class HashMapVsIdentityHashMap {

  public static void main(String[] args) {

    Map<String, Integer> idMap = new IdentityHashMap<>();
    Map<String, Integer> hashMap = new HashMap<>();

    String str = new String("Java");

    idMap.put(str, 1);
    hashMap.put(str, 1);

    boolean isExist = idMap.containsKey("Java"); // false
    boolean isPresent = hashMap.containsKey("Java"); // true

    System.out.println("Does Java exists in IdentityHashmap? : " + isExist);
    System.out.println("Does Java exists in Hashmap? : " + isPresent);

  }

}

Output:
Does Java exist in IdentityHashmap? : false
Does Java exist in Hashmap? : true


You need JDK 7 to run this program because we have used 
diamond operator (<>) to shorten the Generic code, though nothing stops you from being running it on Java SE 6 once you remove the diamond operator and specify the types on right side of initialization as well e.g.

instead of

Map<String, Integer> idMap = new IdentityHashMap<>();
use this

Map<String, Integer> idMap = new IdentityHashMap<String, Integer>();

Using Generic is also one of the Java coding best practices which you should always follow post Java 5. You can see 
Java Coding Guidelines: 75 Recommendations for Reliable and Secure Programs for more of such best practices.

Difference between HashMap and IdentityHashMap in Java



2) Another significant difference between HashMap and IdentityHashMap is that later uses System.identityHashCode() instead of hashCode() method of key object. This means you can also use a mutable object as a key in IdentityHashMap, which is not supported by HashMap in Java. I mean there won't be any compilation error but it will not work as expected i.e. you will not be able to retrieve object back once you modified it state because its hashCode also got changed. Let's see how this work in IdentityHashMap with an example.

Let's assume we have a class called CreditCard, which has a field called expiry, which is nothing but a formatted date in String format. Later we change the CreditCard object by changing it's expiry and see if you can find it out again from both IdentityHashMap and HashMap or not.

Java Program to show difference between HashMap vs IdentityHashMap

import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;

public class sds {

  public static void main(String[] args) {

    CreditCard visa = new CreditCard("VISA", "04/12/2019");
    CreditCard master = new CreditCard("Master", "04/11/2020");
    CreditCard amex = new CreditCard("American Express", "04/10/2021");
   
    Map<CreditCard, String> cardToExpiry = new HashMap<>();
    Map<CreditCard, String> cardToExpiryIdenity = new IdentityHashMap<>();
   
    // inserting objects to HashMap
    cardToExpiry.put(visa, visa.getExpiryDate());
    cardToExpiry.put(master, master.getExpiryDate());
    cardToExpiry.put(amex, amex.getExpiryDate());
   
    // inserting objects to IdentityHashMap
    cardToExpiryIdenity.put(visa, visa.getExpiryDate());
    cardToExpiryIdenity.put(master, master.getExpiryDate());
    cardToExpiryIdenity.put(amex, amex.getExpiryDate());
   
   
    System.out.println("before modifying keys");
    String result = cardToExpiry.get(visa) != null? "Yes" : "No";
    System.out.println("Does VISA card exists in HashMap? " + result);
   
    result = cardToExpiryIdenity.get(visa) != null? "Yes" : "No";
    System.out.println("Does VISA card exists in IdenityHashMap? " + result);
   
    // modifying value object
    visa.setExpiryDate("02/11/2030");
   
    System.out.println("after modifying keys");
    result = cardToExpiry.get(visa) != null? "Yes" : "No";
    System.out.println("Does VISA card exists in HashMap? " + result);
   
    result = cardToExpiryIdenity.get(visa) != null? "Yes" : "No";
    System.out.println("Does VISA card exists in IdenityHashMap? " + result);
  }


}

class CreditCard{
  private String issuer;
  private String expiryDate;
 
 
  public CreditCard(String issuer, String expiryDate) {
    this.issuer = issuer;
    this.expiryDate = expiryDate;
  }


  public String getIssuer() {
    return issuer;
  }


  public String getExpiryDate() {
    return expiryDate;
  }
 
  public void setExpiryDate(String expiry){
    this.expiryDate = expiry;
  }


  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
        + ((expiryDate == null) ? 0 : expiryDate.hashCode());
    result = prime * result + ((issuer == null) ? 0 : issuer.hashCode());
    return result;
  }


  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    CreditCard other = (CreditCard) obj;
    if (expiryDate == null) {
      if (other.expiryDate != null)
        return false;
    } else if (!expiryDate.equals(other.expiryDate))
      return false;
    if (issuer == null) {
      if (other.issuer != null)
        return false;
    } else if (!issuer.equals(other.issuer))
      return false;
    return true;
  }
 
 
}

Output
before modifying keys
Does VISA card exists in HashMap? Yes
Does VISA card exists in IdenityHashMap? Yes
after modifying keys
Does VISA card exists in HashMap? No
Does VISA card exists in IdenityHashMap? Yes


From the output you can see that once you changed the CreditCard object, which is key in both HashMap and IdentityHashMap, you are not able to retrieve object in case of HashMap but you able to retrieve when you use IdentityHashMap because former uses equals() method which return different value once expiry date changed and later uses == operator which return true because in both cases the object is the same in heap.

You can also read
 Core Java Volume 1 - Fundamentals by Cay S. Horstmann to learn more about IdentityHashMap class in Java.

Here are some more important points about IdenityHashMap in Java:

1.     It uses identity methods i.e. equals and hashCode to retrieve values.
2.     It uses reference equality instead of equals() method i.e. object1 == object2 instead of object1.equals(object2).
3.     For hashing, it uses System.identityHashCode(key) instead of key.hashCode() as used by other Map implementations.
4.     The java.util.IdenityHashMap class is used in Serialization and deep copying, where your key is "Class" object or interned String. 

HashMap vs IdenityHashMap in Java


That's all about the difference between IdentityHashMap and HashMap in Java. There are rare cases where you want to use the IdentifyHashMap but it's good to know about it.

No comments:

Post a Comment