Domanda

hashmaps and hash codes question

Have a POJO, over written hashcode and equals to help with a partcular comparator (not shown here)

package coll.hset;

public class Dat {

    private String name;
    private String dat;
    private int aa;//some business reason not used in hashcode and equals

    public int hashCode(){
        int h = 0 ;
        if(name != null){
            h += name.hashCode();
        }
        if(dat != null){
            h += dat.hashCode();
        }
        return h;
    }

    public boolean equals(Object o){
        if(o instanceof Dat){
            Dat oo = (Dat)o;
            if(this.name ==null && oo.name != null){
                return false;
            }else if(!name.equals(oo.name)){
                return false;
            }

            if(this.dat ==null && oo.dat != null){
                return false;
            }else if(!dat.equals(oo.dat)){
                return false;
            }
            return true;
        }
        return false;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDat() {
        return dat;
    }

    public void setDat(String dat) {
        this.dat = dat;
    }

    public int getAa() {
        return aa;
    }

    public void setAa(int aa) {
        this.aa = aa;
    }

}

A user app :

    package coll.hset;

import java.util.HashSet;
import java.util.Random;

public class App {

    final static int SZ = 2 ^ 8;

    /**
     * @param args
     */
    public static void main(String[] args) {
        Random rndm = new Random();// to create random data
        Dat dd;// reference while filling up set
        Dat[] d2 = new Dat[500];// save a few here for later ops
        int fills = 0;
        HashSet<Dat> dats = new HashSet<Dat>();// set
        for (int i = 0; i < 10000; i++) {
            dd = new Dat();
            dd.setAa(i);
            // fill random dat and name.
            char v = (char) (65 + rndm.nextInt(26));
            dd.setDat("a " + v);
            v = (char) (65 + rndm.nextInt(26));
            char v1 = (char) (65 + rndm.nextInt(26));
            char v2 = (char) (65 + rndm.nextInt(26));
            char v3 = (char) (65 + rndm.nextInt(26));
            char v4 = (char) (65 + rndm.nextInt(26));
            dd.setName(v + " " + v1 + v2 + v3 + v1 + v + v4);
            dats.add(dd);
            if (i % 60 == 0) {
                d2[fills++] = dd;
            }

        }
        Dat ref = d2[0];
        int hash = hash(ref.hashCode());
        int idx = indexFor(hash, SZ);
        boolean has1 = dats.contains(d2[0]);
        System.out.println("has d 0 :" + has1 + ", name :" + ref.getName() + ", hash :" + ref.hashCode() + ". hash2 :" + hash + ", idx :" + idx + ", when size of table :" + SZ);

        d2[0].setName(ref.getName() + "l");
        // d2[0].setName(ref.getName() + "l");
        d2[0].setName("Tony G");
        // ref.setDat("sd=");
        hash = hash(ref.hashCode());
        // if you run this many times will see that for some cases the table is the same, so a quicker rehash, instead of remove and add back after change is what I'm after
        idx = indexFor(hash, SZ);
        has1 = dats.contains(d2[0]);
        System.out.println("has d 0 after name change :" + has1 + ", name :" + ref.getName() + ".");
        System.out.println("has d 0 :" + has1 + ", name :" + ref.getName() + ", hash :" + ref.hashCode() + ". hash2 :" + hash + ", idx :" + idx + ", when size of table :" + SZ);
        System.out.println(" at : " + new java.util.Date());

    }

    static int hash(int h) {
        // From Sun Java impl
        /*
         * / This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor).
         */
        h ^= (h >>> 20) ^ (h >>> 12);

        return h ^ (h >>> 7) ^ (h >>> 4);

    }

    static int indexFor(int h, int length) {
        return h & (length - 1);
    }
}

As expected 2nd search says out Dat object d2[0] is not there in set even thought it is. I know how to fix it - one way - remove it, change it and then add it back. Is there any other way to tell the set that we are mutating a particular object?

From http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.remove%28java.lang.Object%29

Can see how the Oracle/Sun Java HashMap rehashes it self. Question is - can we add a new method that tells the set - please rehash this object, instead of removing and adding it back, so its more efficient.

If you run above code many times will see that for some cases the table is the same (for the before and after hash code for mutated object), so a quicker rehash, instead of remove and add back after change is what I'm after, that takes advantage of that fact and only rehashes if bucket changes.

È stato utile?

Soluzione

Object's hash assumed to be constant during the object's lifetime, so strict answer to your question is: no. When you modify your object such that it's hash code is changed, you'd better remove it from map and add it back again.

Altri suggerimenti

Whenever it the hashcode() function is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.so as @kiril suggested remove it from map and add it back again.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top