user695652
user695652

Reputation: 4275

Null-value when iterating through Hash key set

It puzzles me how the following segment can lead to a null value of the Boolean mandatory, although it is not null at the corresponding key in the actual hashtable:

for (List<List<A>> a : hashMap.keySet()) {  
    Boolean mandatory = hashMap.get(a);
}

Upvotes: 2

Views: 4173

Answers (3)

wolfcastle
wolfcastle

Reputation: 5930

Using a mutable object for a Map key is always a dangerous thing. If you maintain any reference to any of those keys after inserting into the map, then it is very likely that one of those keys will be modified at some point in the future which will invalidate the contents of your map.

A less likely, but possible scenario, even assuming you somehow don't screw up your List<List<>> key is if you have messed up the equals method of class A, then your Lists' equals method will also be messed up, again screwing up your map.

Look at alphazero's nice code example if you need further proof that what you are attempting to do is a bad idea.

Upvotes: 0

alphazero
alphazero

Reputation: 27234

A HashMap will return null if the key specified is not bound to a value.

Issue is almost certainly that comparison op on a -- a List -- against keys is failing.

Let me guess: are you modifying these lists (the key object) after you have called a put? Did you remove all entries in one of the keys? Remember an empty list is equal to all empty ArrayLists. Further remember that List.equals() compares list content (one by one) to test equality.

package sof_6462281;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Demonstrate the fact that the Map uses key.equals(k) to
 * test for key equality.  Further demonstrate that it is a 
 * very bad idea to use mutable collections are keys to maps.
 */
public class ListAsKey {
    public static void main(String[] args) {
        Map<List<A>, Boolean>  map = new HashMap<List<A>, Boolean>();

        List<A> alist = new ArrayList<A>();
        map.put(alist, true);
        for (List<A> a : map.keySet()) {
            Boolean b = map.get(a);
            System.out.format("\t%s(ArrayList@%d) => %s\n",a, a.hashCode(), map.get(a)); 
        }

        // you changed your list after the put, didn't you?
        alist.add(new A());
        for (List<A> a : map.keySet()) {
            Boolean b = map.get(a);
            System.out.format("\t%s(ArrayList@%d) => %s\n",a, a.hashCode(), map.get(a)); 
        }

        alist.clear();
        for (List<A> a : map.keySet()) {
            Boolean b = map.get(a);
            System.out.format("\t%s(ArrayList@%d) => %s\n",a, a.hashCode(), map.get(a)); 
        }
    }
    public static final class A { /* foo */ }
}

Results:

[](ArrayList@1) => true
[sof_6462281.ListAsKey$A@4b71bbc9](ArrayList@1265744872) => null
[](ArrayList@1) => true

edit: added more ops to above and added console out.

Upvotes: 2

Craig Suchanec
Craig Suchanec

Reputation: 10824

Boolean can be null because it wraps the value-type primitive boolean. I am unsure what you mean its not null at the corresponding key in the actual hashtable. You are iterating over the keys then getting the values at those keys. The value at a key was inserted as null so when you are retrieving it you are getting the null.

Upvotes: 0

Related Questions