Sometime back I have a special case in which I have to compare Map’s Key based on equality operator (==)
. The equality operator (==) compares the references (addresses in memory) of the two Keys as two different numbers.
In other hand, HashMap is most used Java Collection Framework component which compares uniqueness of the Key with the help of equals()
method.
Also, IdentityHashMap
does not use hash from object.hashCode()
but uses System.identityHashCode(object)
. We could use IdentityHashMap for mutable objects for whose hash code changes during the runtime.
If you want to learn more on equals()
and ==
which applies on String Object
then follow this tutorial: https://crunchify.com/how-to-override-equals-and-hashcode-method-in-java/.
Basic Test which demonstrate above equals() and == behavior:
package crunchify.com.tutorials; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; /** * @author Crunchify.com * */ public class CrunchifyIdenityHashMapVsHashMapSample { public static void main(String[] args) { Map<String, String> crunchifyIdentityHashMap = new IdentityHashMap<String, String>(); Map<String, String> crunchifyHashMap = new HashMap<String, String>(); // Let's checkout what happens when we put Unique Key to IdentityHashMap crunchifyIdentityHashMap.put("Company", "Crunchify"); // this considered different object for == operator crunchifyIdentityHashMap.put(new String("Company"), "Google"); crunchifyIdentityHashMap.put("Company", "Facebook"); System.out.println("crunchifyIdentityHashMap KeySet Size: " + crunchifyIdentityHashMap.keySet().size()); // Let's checkout what happens when we put Unique Key to HashMap crunchifyHashMap.put("Company", "Crunchify"); // key1.equals(key2) returns true hence it removes the old value crunchifyHashMap.put(new String("Company"), "Google"); crunchifyHashMap.put("Company", "Facebook"); System.out.println("crunchifyHashMap KeySet Size: " + crunchifyHashMap.keySet().size()); } }
Result:
crunchifyIdentityHashMap KeySet Size: 2 crunchifyHashMap KeySet Size: 1
Let’s do Performance Testing on both Maps:
- Create Java Class:
CrunchifyIdentityHashMapVsHashMapPerformance
.java
startCrunchifyTest()
- Generates Random Map size in millions
- Instantiate and Initialize
crunchifyString[]
String Array object with above generated Random Number with text:This is Crunchify's Test # number
crunchifyCompareIdentityHashMapVsHashMap(String[] crunchifyString, Map<String, Integer> crunchifyMap, String name)
- Pass all required parameters to this method
crunchifyMap
will have value IdentityHashMap / HashMap- Iterate through crunchifyString[] and put values to Map — this operation takes some time
- Iterate through crunchifyString[] and get values from Map — this operation takes some time
- We will find out the execution time for both above operation so we could compare which one is better for above operations? IdentityHashMap OR HashMap
- Print above result
- Perform above tasks 2 and 3 total 8 times.
package crunchify.com.tutorials; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Random; /** * @author Crunchify.com * */ public class CrunchifyIdentityHashMapVsHashMapPerformance { static Random rand = new Random(); private static void startCrunchifyTest() { // Let's run test for 5 times for (int i = 0; i < 15; ++i) { // Let's create random Map size which we will use in IdentityHashMap and HashMap int randomMapSize = 1000000 + rand.nextInt(9000000); String[] crunchifyString = new String[randomMapSize]; for (int j = 0; j < randomMapSize; ++j) // Assign below string to crunchifyString Object crunchifyString[j] = "This is Crunchify's Test #" + j; System.out.println("\nIteration # " + i + " - Creating String with size: " + randomMapSize); crunchifyCompareIdentityHashMapVsHashMap(crunchifyString, new HashMap<String, Integer>(randomMapSize), "HashMap"); // Runs the garbage collector System.gc(); crunchifyCompareIdentityHashMapVsHashMap(crunchifyString, new IdentityHashMap<String, Integer>(randomMapSize), "IdentityHashMap"); // Runs the garbage collector System.gc(); } } /** * @param crunchifyString * @param crunchifyMap * : IdentityHashMap / HashMap * @param name */ private static void crunchifyCompareIdentityHashMapVsHashMap(String[] crunchifyString, Map<String, Integer> crunchifyMap, String name) { long start = System.currentTimeMillis(); // put crunchifyString String[] to map for (int put = 0; put < crunchifyString.length; ++put) crunchifyMap.put(crunchifyString[put], put); boolean result = false; for (int get = 0; get < crunchifyString.length; ++get) { if (crunchifyMap.get(crunchifyString[get]) != get) result = true; } System.out.println(name + " time taken : \t" + (System.currentTimeMillis() - start) / 1000. + " sec"); // Check for result discrepancy if (crunchifyMap.size() != crunchifyString.length) System.out.println("Please check size. Test failed"); if (result) System.out.println("Result failed.."); } public static void main(String[] args) { System.out.println("IdentityHashMap Vs. HashMap comparison Test started..."); // method to compare IdentityHashMap and HashMap startCrunchifyTest(); } }
Result:
IdentityHashMap Vs. HashMap comparison Test started... Iteration # 0 - Creating String with size: 6964175 HashMap time taken : 3.155 sec IdentityHashMap time taken : 1.517 sec Iteration # 1 - Creating String with size: 6556459 HashMap time taken : 3.415 sec IdentityHashMap time taken : 1.466 sec <== IdentityHashMap gives better result for large object Iteration # 2 - Creating String with size: 9567664 HashMap time taken : 4.173 sec IdentityHashMap time taken : 2.339 sec <== better Iteration # 3 - Creating String with size: 4230755 HashMap time taken : 0.372 sec IdentityHashMap time taken : 0.911 sec Iteration # 4 - Creating String with size: 7821718 HashMap time taken : 1.096 sec IdentityHashMap time taken : 0.812 sec Iteration # 5 - Creating String with size: 8125421 HashMap time taken : 4.883 sec IdentityHashMap time taken : 1.876 sec <== better Iteration # 6 - Creating String with size: 3166432 HashMap time taken : 0.537 sec IdentityHashMap time taken : 0.708 sec Iteration # 7 - Creating String with size: 2821415 HashMap time taken : 0.227 sec IdentityHashMap time taken : 0.621 sec
Observation:
As you can see here in result, for large maps IdentityHashMap performs much better. Why? IdentityHashMap doesn't use equals() and hashcode() methods
, which are considered very costly.
Just FYI:
Above operations which we are doing for putting and getting values from and into Map are very CPU intensive.