This is some information on the Java 6 certification that I prepare. This information is not very well structured and it is most of the time an answer to the questions that come when I read the SCJP 6 book (Bates and Sierra). *** AS OF 5 OF MARCH 2010, I AM SCJP 6.0 ***

Saturday, January 16, 2010

19 - hashCode - level basic - Java 1.6.0_17 - NB 6.8

TO BE REPHRASED !!!

To summarize

It is not enough to override "equals" and then try to retrieve keys in a map... If the 2 objects are equal, then the hash codes have to be the same. I suppose they play a max on this one...

More on this topic

You put an object in a map(the key). Then you change the object. And you base the hash code on the length of the String... The key changed but the method "get" will look in the bucket corresponding to the new hash code and will not find the value...

pseudo code

key1, value1
length("key1") => bucket 4
then you change key1 in "key11"
the object is still in bucket 4 but the hash code will reference the bucket5 !!! because lenght("key11") is... 5 !!!
It is not a good idea to change the key... I pref remove and then add...

To understand the statement on p 554, I propose to study the following example :

1) equals is overridden and hashCode is not.
2) equals is overridden and and hashCode is overridden and returns always 0.
3) equals is overridden and hashCode is overridden and returns the length of the string.

package mapandkeys;


public class Dog {

    String name;

    public Dog(String n) {
        name = n;
    }

    public boolean equals(Object o) {
        System.out.println("accessing method equals");
        if((o instanceof Dog) && (((Dog)o).name == name)) {
           return true;
        }
        return false;
    }


hasCode NOT OVERRIDDEN

OR

//    public int hashCode() {
//        System.out.println("accessing method hashcode");
//        return 0;
//
//    }

OR

//    public int hashCode() {
//        System.out.println("accessing method hashcode");
//        return name.length();
//
//    }


     public String toString() {
         return name;
     }
}


package mapandkeys;

public class Cat {
}

package mapandkeys;

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

public class Main {

    public static void main(String[] args) {
        Map m = new HashMap();
        m.put("k1", "k1");
        m.put("k2", "k2");
        System.out.println("1 size:" + m.size());

        String aString = (String) m.get("k1");

The 3 will return "k1"
default : it is the same "k1" from the string pool and hashCode will return the same id
0 : all objects will return the same bucket (bucket 0)
lenght : have the same length (bucket 2)

        System.out.println("2 aString : " + aString);

        m.put(new Dog("dog1"), "dog1");
        m.put(new Dog("dog2"), "dog2");
        System.out.println("3 size:" + m.size());
        aString = (String) m.get(new Dog("dog1"));
        System.out.println("4 aString : " + aString);
        aString = (String) m.get(new Dog("dog2"));
        System.out.println("5 aString : " + aString);

default : You do a new 2 times and the default hashCode will return 2 diffent hash ! You will not be able to retrieve the values !!!
0 and length will return the hash same thus we can retrieve the keys.

        Cat aCat = new Cat();
        System.out.println("hash of aCat :" + aCat.hashCode());
        m.put(aCat, "cat1");
        System.out.println("6 size:" + m.size());
        aString = (String) m.get(aCat);
        System.out.println("7 aString : " + aString);

Nothing was overridden in the Cat class and you do a "get" on the same cat (same object and same hash code) so the "cat1" is returned.

        Dog d1Key = new Dog("cloverkey");
        Dog d1Value = new Dog("clovervalue");
        m.put(d1Key, d1Value);
        System.out.println("8 size:" + m.size());
        System.out.println("9 d1Value : " + m.get(d1Key));

It is the same object and can be found in the 3 cases.

        d1Value.name = "clovervalue1";
        System.out.println("10 size:" + m.size());
        System.out.println("11 d1Value : " + m.get(d1Key));

The value was changed but not the key. The 3 will return the same (new) value.

        d1Key.name = "1";

The key is changed !!! It was "cloverkey" and now it is "1" !

        System.out.println("12 d1Value() : " + m.get(d1Key));

This time we change the key ! The hash code will make a difference...
def and 0, both point to the same bucket
length("clovervalue") and length("1") are different... Java will try to find the key in the wrong bucket !

        Dog d2 = new Dog("1");
        System.out.println("13 d1Value() : " + m.get(d2));

default : it is a different object because we used the keyword new !
0 : OK They all use the bucket 0 and it is not performant !
lenght : NOK because the length("clovervalue") = 11  and length("1") = 1 the value is in bucket 11 and we try to find the value in bucket 1 !
If you use 0 you put all the keys and values in the same bucket !

        System.out.println("14 hash of 12:" + d1Key.hashCode());
        System.out.println("15 hash of 13:" + d2.hashCode());

      
        System.out.println("14 contains");
        if(m.containsKey(d1Key)) {
            System.out.println("true");
        }

    }
}


hashcode in Dog is not overriden

run:
1 size:2
2 aString : k1
3 size:4
4 aString : null
5 aString : null
hash of aCat :12677476
6 size:5
7 aString : cat1
8 size:6
9 d1Value : clovervalue
10 size:6
11 d1Value : clovervalue1
12 d1Value() : clovervalue1
13 d1Value() : null
14 hash of 12:33263331
15 hash of 13:6413875
14 contains
true
BUILD SUCCESSFUL (total time: 0 seconds)

hashCode is and returns 0

run:
1 size:2
2 aString : k1
accessing method hashcode
accessing method hashcode
accessing method equals
3 size:4
accessing method hashcode
accessing method equals
accessing method equals
4 aString : dog1
accessing method hashcode
accessing method equals
5 aString : dog2
hash of aCat :11394033
6 size:5
7 aString : cat1
accessing method hashcode
accessing method equals
accessing method equals
8 size:6
accessing method hashcode
9 d1Value : clovervalue
10 size:6
accessing method hashcode
11 d1Value : clovervalue1
accessing method hashcode
12 d1Value() : clovervalue1
accessing method hashcode
accessing method equals
13 d1Value() : clovervalue1
accessing method hashcode
14 hash of 12:0
accessing method hashcode
15 hash of 13:0
14 contains
accessing method hashcode
true

hasCode is and returns name.lenght()

run:
1 size:2
2 aString : k1
accessing method hashcode
accessing method hashcode
accessing method equals
3 size:4
accessing method hashcode
accessing method equals
accessing method equals
4 aString : dog1
accessing method hashcode
accessing method equals
5 aString : dog2
hash of aCat :11394033
6 size:5
7 aString : cat1
accessing method hashcode
8 size:6
accessing method hashcode
9 d1Value : clovervalue
10 size:6
accessing method hashcode
11 d1Value : clovervalue1
accessing method hashcode
12 d1Value() : null
accessing method hashcode
13 d1Value() : null
accessing method hashcode
14 hash of 12:1
accessing method hashcode
15 hash of 13:1
14 contains
accessing method hashcode



1 comment:

  1. Hi
    Let's do sth like this :

    "The value was changed but not the key. The 3 will return the same (new) value."

    d1Key.name = "cloverkeZ";

    And this will be found because d1Key is still the same object :-). There is an error in SCJP 6 book (Bates and Sierra).

    Page 586 "the hashcode test succeeds, but the equals() test
    fails because arthur is NOT equal to clover."
    - it is not true.

    equals will not be even called because d1 is still same object

    The object will be found if there is the same length of elements - method m.get use hashCode which compare keys using "==" and because d1Key is only one object it will returned.

    ReplyDelete

Followers