Tuesday, November 30, 2010

Java's System.identityHashCode

The java.lang.System class provides many useful general utilities including handles to the standard output stream, the standard input stream, the standard error stream, and the console as well as methods for obtaining the current time in milliseconds, defined properties, and environmental variables. In this blog post, I briefly look at the System.identityHashCode(Object) method.

The Javadoc documentation for System.identityHashCode(Object) states:
Returns the same hash code for the given object as would be returned by the default method hashCode(), whether or not the given object's class overrides hashCode().
The System.identityHashCode(Object) method provides the hash code of the provided Object as would be returned from its ultimate Object parent regardless of whether the passed-in object overrode the hashCode method.  This is demonstrated by the following simple class, the source code of which is followed by sample output.

Main.java
package dustin.examples;

import static java.lang.System.out;

public class Main
{
   private static final String NEW_LINE = System.getProperty("line.separator");

   /**
    * Print the overridden and identity hash codes of the provided object.
    *
    * @param object Object whose overridden and identity hash codes are to be
    *    printed to standard output.
    */
   public static void printHashCodes(final Object object)
   {
      out.println(
           NEW_LINE + "====== "
         + String.valueOf(object) + "/"
         + (object != null ? object.getClass().getName() : "null")
         + " ======");
      out.println(
           "Overridden hashCode: "
         + (object != null ? object.hashCode() : "N/A"));
      out.println("Identity   hashCode: " + System.identityHashCode(object));
   }

   /**
    * Main executable function.
    *
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final Integer int1 = 1;
      final int int2 = 1;
      final Long long1 = 1L;
      final long long2 = 1L;
      final String str = "SomeString";
      final String nullStr = null;
      final SimpleData simpleData = new SimpleData("AnotherString");

      printHashCodes(int1);
      printHashCodes(int2);
      printHashCodes(long1);
      printHashCodes(long2);
      printHashCodes(str);
      printHashCodes(nullStr);
      printHashCodes(simpleData);
   }
}


Output from Main.java
====== 1/java.lang.Integer ======
Overridden hashCode: 1
Identity   hashCode: 4072869

====== 1/java.lang.Integer ======
Overridden hashCode: 1
Identity   hashCode: 4072869

====== 1/java.lang.Long ======
Overridden hashCode: 1
Identity   hashCode: 1671711

====== 1/java.lang.Long ======
Overridden hashCode: 1
Identity   hashCode: 1671711

====== SomeString/java.lang.String ======
Overridden hashCode: 728471109
Identity   hashCode: 11394033

====== null/null ======
Overridden hashCode: N/A
Identity   hashCode: 0

====== AnotherString/dustin.examples.SimpleData ======
Overridden hashCode: 1641745
Identity   hashCode: 1641745


The example code and its corresponding output indicate that the identity hash code returned by System.identityHashCode(Object) can be different than the object's hash code defined by an overridden version of the hashCode() method. It's also worth noting that if a class does not override hashCode(), then its own hash code will obviously match that provided by Object. In the example above, the SimpleData class (not shown here) does not have a hashCode implementation, so its own hashCode IS the Object-provided hashCode (identity hash code). Another observation that can be made based on the above example is that System.identityHashCode(Object) returns zero when null is passed to it.

Bug 6321873 explains that the hash code returned by System.identityHashCode(Object) is not guaranteed to be unique (it's a hash code after all). That being stated, there are situations in which the hash code returned by System.identityHashCode(Object) is "unique enough" for the problem at hand. For example, Brian Goetz et al. demonstrate use of this to prevent deadlocks in Java Concurrency in Practice.

There are numerous online resources providing greater detail regarding this method. Erik Engbrecht describes how the identity hash code is calculated in his post System.identityHashCode (a topic further discussed in the StackOverflow thread "How does the JVM ensure that System.identityHashCode() will never change?"). Rosarin Roy provides a brief overview of the method in the post System.identityHashCode() - What Is It?

In this blog post, I have looked at a common method that I only use rarely, but which is worth noting for its occasional use. The System.identityHashCode(Object) method is an easy way to obtain the hash code that would have been provided for a particular object if that object's class did not override the hashCode() implementation. The Javadoc documentation for Object.hashCode() tells us that this identity hash code is often, but is not required to be, the memory address of the object converted to an integer.

1 comment:

Unknown said...

Note, in your examples the identity hash code doesn't appear to be literally the memory address. The first identity hash, 4072869, is odd. An actual address will be aligned to at least four bytes, and almost certainly a higher power of two. It will be the address transformed with a simple shuffle to get the bits mixed up.