Tuesday, December 25, 2012

Collections : Keys in a Map 1

Some time ago I was needed to write my own custom classes for keys in Maps. That brought me to the concept of immutable classes which are needed to be valid keys in a Map. 

Let us look at the code below. It is an inner class which has a state-holding property of type String which is set through the Constructor. In addition to that it overrides the methods with which the HashMap or any other implementation of Map would 'bucket' it and check the equality with a future retrieval. 
 
    /**
     *
     * Invalid because this has a mutable property key of type String. It is mutable as it has a setter method.
     *
     * @author Sanjay
     *
     */
    static class InvalidKey {
        private String key;

        /**
         *
         *
         * @return
         */
        public String getKey() {
            return key;
        }

        /**
         * The spoiler method
         *
         * @param key
         */
        public void setKey(String key) {
            this.key = key;
        }


        /**
         *
         *
         * @param key
         */
        public InvalidKey(String key) {
            super();
            this.key = key;
        }
       
        /**
         *
         * We re-use the String equals
         *
         */
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof InvalidKey) {
                if(((InvalidKey)obj).getKey().equals(this.key)) return true;
                else return false;
            }
            else return false;
        }
       
        /**
         *
         * We re-use the Object hashCode and the String code hashCode if the key is not null.
         *
         */
        @Override
        public int hashCode() {
            if(key!=null) return this.key.hashCode(); // we simply return String's hash code
            return super.hashCode(); // if key is null then we return the Object's hash code value.
        }
       
        /**
         *
         *
         */
        public String toString() {
            StringBuffer buffer = new StringBuffer("");
            buffer.append(this.key);
            return buffer.toString();
        }
       
    }



The problem with the above class is that the setter method with the javadoc comment Spolier method can be used to reset the Key after the Map has been modified with this key. An example of the use will elucidate the problem.
package org.home.project21.study.designPatterns;

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

/**
 *
 *
 * Collections : Keys in a Map 1
 *
 *
 *
 * @author Sanjay
 *
 */
public class CollectionsTest1 {


    /**
     * @param args
     */
public static void main(String[] args) {
        // TODO Auto-generated method stub
        Map myMap = new HashMap();
        InvalidKey iKey = new InvalidKey("InvalidKey1");
        InvalidKey iKeyRef = new InvalidKey("InvalidKey1"); // creating a new key with the same value
        myMap.put(iKey, "This is Value");
        iKey = null; // we remove the link from the first reference to the Object
        System.out.println(" myMap has  " + myMap.get(iKeyRef) + " against this key " + iKeyRef.toString());// this will print the correct value
        iKeyRef.setKey("InvalidKey2");
        System.out.println(" myMap has  " + myMap.get(iKeyRef) + " against this key " + iKeyRef.toString());// this will print the correct value
}


The following is the output of the Program.
myMap has This is Value against this key InvalidKey1 
myMap has null against this key InvalidKey2 As can be seen from the output the key is unable to retrieve the Value from the Map as the Object is mutable and has it's state changed. So the equality test fails and hence a null is returned.

No comments: