Identity HashMap

The IdentityHashMap class provides a Map implementation that uses reference equality (==) instead of object equality (equals()) when comparing keys and values. This is an intentional violation of Map's general contract, which mandates the use of equals() when comparing elements.

IdentityHashMap obtains hash codes via System's static int identityHashCode(Object x) method instead of via each key's hashCode() method. identityHashCode() returns the same hash code for x as returned by Object's hashCode() method, whether or not x's class overrides hashCode(). The hash code for the null reference is zero.

These characteristics give IdentityHashMap a performance advantage over other Map implementations. Also, IdentityHashMap supports mutable keys (objects used as keys and whose hash codes change when their field values change while in the map). Listing 8-26 contrasts IdentityHashMap with HashMap where mutable keys are concerned.

Listing 8-26. Contrasting IdentityHashMap with HashMap in a mutable key context import java.util.IdentityHashMap; import java.util.HashMap; import java.util.Map;

new IdentityHashMap<Employee, String>(); new HashMap<Employee, String>();

public class IdentityHashMapDemo {

public static void main(String[] args) {

Map<Employee, String> mapl Map<Employee, String> map2 Employee el = new Employee("John Doe", 28); map1.put(e1, "SALES"); System.out.println(mapl); Employee e2 = new Employee("Jane Doe", 26); map2.put(e2, "MGMT"); System.out.println(map2); System.out.println("map1 contains key el = ' System.out.println("map2 contains key e2 = ' el.setAge(29); e2.setAge(27); System.out.println(mapl); System.out.println(map2);

System.out.println("map1 contains key el = " + mapl.containsKey(el)); System.out.println("map2 contains key e2 = " + map2.containsKey(e2));

class Employee {

private String name; private int age;

Employee(String name, int age) {

^Override public boolean equals(Object o) {

if (!(o instanceof Employee))

return false; Employee e = (Employee) o; return e.name.equals(name) && e.age == age;

^Override public int hashCode() {

int hashCode = 19;

hashCode = hashCode*31+name.hashCode(); hashCode = hashCode*31+age; return hashCode;

void setAge(int age) {

void setName(String name) {

this.name = name;

^Override public String toString()

Listing 8-26's main() method creates IdentityHashMap and HashMap instances that each store an entry consisting of an Employee key and a String value. Because Employee instances are mutable (because of setAge() and setName()), main() changes their ages while these keys are stored in their maps. These changes result in the following output:

{John Doe 28=SALES} {Jane Doe 26=MGMT} mapl contains key el = true map2 contains key e2 = true {John Doe 29=SALES} {Jane Doe 27=MGMT} mapl contains key el = true map2 contains key e2 = false

The last four lines show that the changed entries remain in their maps. However, map2's containsKey() method reports that its HashMap instance no longer contains its Employee key (which should be Jane Doe 27), whereas mapl's containsKey() method reports that its IdentityHashMap instance still contains its Employee key, which is now John Doe 29.

NOTE: IdentityHashMap's documentation states that "a typical use of this class is topology-preserving object graph transformations, such as serialization or deep copying." (I discuss serialization in Chapter 10.) It also states that "another typical use of this class is to maintain proxy objects." Also, developers responding to stackoverflow's "Use Cases for Identity HashMap" topic (http://stackoverflow.com/questions/838528/use-cases-for-identity-hashmap) mention that it is much faster to use IdentityHashMap than HashMap when the keys are Class objects.

Was this article helpful?

0 0

Post a comment