Monday, July 5, 2021

Difference between HashMap vs IdentityHashMap in Java? Example

The IdentityHashMap is one of the lesser known Map implementations from JDK. Unlike general purposes Map implementations like HashMap and LinkedHashMap, it is very special and its internal working is quite different than HashMap. The main difference between IdentityHashMap and HashMap in Java is that the former uses the equality operator (==) instead of the equals() method to compare keys. This means you need the same key object to retrieve the value from IdentityHashMap, you cannot retrieve values by using another key that is logically equal to the 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 keys in Map whose hash codes 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 the key later will not be the same as before. Even if you return the same, the equals() method will not return true when you compare key objects from entry to the given key objects.

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 it. Since most 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 the == 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 are same in heap space. It will return false even if both objects have the 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 the 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 these free Java programming courses for more of such best practices.



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 like you will not be able to retrieve the object back once you modified it state because its hashCode also got changed. Let's see how this works in IdentityHashMap with an example.

HashMap vs IdenityHashMap in Java





Java Program for difference between HashMap vs IdentityHashMap

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 its expiry and see if you can find it out again from both IdentityHashMap and HashMap or not.
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 an object in the case of HashMap but you able to retrieve it when you use IdentityHashMap because the former uses an equal () method which returns different value once expiry date changed and later uses == operator which returns true because in both cases the object is the same in heap.

You can also see these free Java coding courses 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 a 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. 

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. Just keep in mind that instead of using the equals() method it uses reference equality to store and retrieve data from Map. 


Related Java HashMap tutorials  you may like
  • How does get() method of HashMap work in Java? (answer)
  • What is the difference between HashMap and Hashtable in Java? (answer)
  • What is the difference between ArrayList and HashMap in Java? (answer)
  • What is the difference between HashSet and HashMap in Java? (answer)
  • Difference between ConcurrentHashMap and HashMap in Java? (answer)
  • How HashSet internally works in Java? (answer)
  • How ConcurrentHashMap internally works in Java? (answer)

No comments:

Post a Comment

Feel free to comment, ask questions if you have any doubt.