0

I have been having some trouble using the function containsKey. I wrote a small program to show where I am expecting containsKey to give me a different result:

HashMap<IdentifierInterface, Set<NaturalNumberInterface>> hashMap;
HashMap<StringBuffer, Integer> works;

TryHashmap(){
    hashMap = new HashMap<IdentifierInterface, Set<NaturalNumberInterface>>();
    works = new HashMap<StringBuffer, Integer>();
}
private void start() {      
    Identifier iden = new Identifier('a');
    NaturalNumber nn = new NaturalNumber('8');
    Set<NaturalNumberInterface> set = new Set<NaturalNumberInterface>();
    set.insert(nn);

    hashMap.put(iden, set);
    System.out.println(hashMap.containsKey(iden));

    Identifier newIden = new Identifier('a');
    System.out.println(hashMap.containsKey(newIden)); //TODO why is this not true?

    iden.init('g');
    System.out.println(hashMap.containsKey(iden));
}

public static void main(String[] argv) {
    new TryHashmap().start();
}

The constructor of the Identifier class is as follows, the init() is similar but it will remove anything that was in the identifier before.

Identifier(char c){
    iden = new StringBuffer();
    iden.append(c);
}

I put something into the hashmap using an Identifier as key, but when I try to use an Identifier with a different name but with the same content the containsKey function returns false where I am expecting a true. (the output prints true false true)

Thanks in advance!

Marnix
  • 3
  • 3

2 Answers2

1

Implement equals() and hashCode() for the identifier object. hashCode is needed to find the relevant bucket and equals is required to handle collisions while hashing.

Further Reading

Rubbal
  • 779
  • 7
  • 19
  • 1
    Ideally, your keys should be `Immutable`. So if you are modifying the key in some way after insertion, you will again run into similar issues. – Rubbal Jan 19 '17 at 05:52
  • I looked up those methods, if I understand correctly I need to make a function (in the identifier class) that returns a boolean on whether the identifiers are the same. Would that be something like: public boolean equals(IdentifierInterface iden1, IdentifierInterface iden2) As for the hashcode, would you be able to give me a hint on how to implement it. The information about the hashcode function doesn't make it any clearer to me... – Marnix Jan 19 '17 at 06:22
  • In this case, you can simply return `Character.hashCode(c)`. Note that you should not change the buffer later on as the object is identified by `c` in the argument. – Rubbal Jan 19 '17 at 06:45
  • If you're using an IDE, most of them have generate `equals()` and `hashCode()` ability. – Rubbal Jan 19 '17 at 06:46
  • Thanks for all the help, unfortunately I just couldn't get the hashCodes() to be the same for Identifiers with the same values in them. I temporarily solved it by converting the identifier to a string (I made a toString function) and using that as a key instead. – Marnix Jan 19 '17 at 08:52
0

method containsKey in HashMap.class

/**
 * Returns <tt>true</tt> if this map contains a mapping for the
 * specified key.
 *
 * @param   key   The key whose presence in this map is to be tested
 * @return <tt>true</tt> if this map contains a mapping for the specified
 * key.
 */
public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

method getEntry in HashMap.class

   /** 
     * Returns the entry associated with the specified key in the 
     * HashMap.  Returns null if the HashMap contains no mapping 
     * for the key. 
     */  
    final Entry<K,V> getEntry(Object key) {  
        int hash = (key == null) ? 0 : hash(key.hashCode());  
        for (Entry<K,V> e = table[indexFor(hash, table.length)];  
             e != null;  
             e = e.next) {  
            Object k;  
            if (e.hash == hash &&  
                ((k = e.key) == key || (key != null && key.equals(k))))  
                return e;  
        }  
        return null;  
    }  

the method getEntry told us that the result will be true, only if the Object a has the same hashCode() as the Object b and a.equals(b)

  • I think I got the equals down. Only the hashCode() is not totally clear to me. – Marnix Jan 19 '17 at 06:43
  • I see that System.out.println(iden.hashCode()); and System.out.println(newIden.hashCode()); are producing different hashcodes which need to be the same in order for the getEntry to work properly. I looked for answers and got to this site https://coderanch.com/t/612036/java/hashcode-equal-objects but there is seems that two different object with the same value do get the same hashcode. Why don't my identifier object with the same values have the same hashcode as well? – Marnix Jan 19 '17 at 06:47
  • @Marnix because if you override the method equals() properly, the two object must have same hashcode. a.equals(b) -> a.hashCode() == b.hashCode(). That is what the 'If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. ' really means – stackoverflow Jan 19 '17 at 07:32
  • @Marnix you are using ‘’Identifier iden = new Identifier('a'); ‘’ and ‘’ Identifier newIden = new Identifier('a'); ‘’ and the method 'getEntry() ' has " (k = e.key) == key " . If you check "iden == newIden", it must be a false. I think this is the root cause. – stackoverflow Jan 19 '17 at 07:44
  • Just as same as this " String aString = new String("a"); String bString = new String("a"); System.err.println(aString == bString);" – stackoverflow Jan 19 '17 at 07:46