Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Sets as keys in Java Maps

I have a Map that uses a Set for the key type, like this:

Map<Set<Thing>, Val> map;

When I query map.containsKey(myBunchOfThings), it returns false, and I don't understand why. I can iterate through each key in the keyset and verify there is a key that (1) has the same hashCode, and (2) is equals() to myBunchOfThings.

System.out.println(map.containsKey(myBunchOfThings)); // false.
for (Set<Thing> k : map.keySet()) {
  if (k.hashCode() == myBunchOfThings.hashCode() && k.equals(myBunchOfThings) {
     System.out.println("Fail at life."); // it prints this.
  }
}

Do I just fundamentally misunderstand the contract for containsKey? Is there a secret to using sets (or more generally, collections) as keys to maps?

like image 935
Gabe Johnson Avatar asked Mar 06 '10 17:03

Gabe Johnson


People also ask

Can a set be a key for map in Java?

HashMap. keySet() method in Java is used to create a set out of the key elements contained in the hash map. It basically returns a set view of the keys or we can create a new set and store the key elements in them. Parameters: The method does not take any parameter.

Can we use set as key in map?

A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on a such a map.

Can I use HashSet as the key of HashMap?

Therefore, to use an object as a key in HashMap , HashSet , or Hashtable in Java, we need to override equals and hashcode methods of that object since default implementation of these methods simply check for the instance equality.

How do you set a key-value on a map?

put() method of HashMap is used to insert a mapping into a map. This means we can insert a specific key and the value it is mapping to into a particular map. If an existing key is passed then the previous value gets replaced by the new value. If a new pair is passed, then the pair gets inserted as a whole.


1 Answers

Key should not be mutated while used in the map. The Map java doc says:

Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on a such a map.

I knew this issue, but never made the test until now. I elaborate then a bit more:

   Map<Set<String>, Object> map  = new HashMap<Set<String>, Object>();

   Set<String> key1 = new HashSet<String>();
   key1.add( "hello");

   Set<String> key2 = new HashSet<String>();
   key2.add( "hello2");

   Set<String> key2clone = new HashSet<String>();
   key2clone.add( "hello2");

   map.put( key1, new Object() );
   map.put( key2, new Object() );

   System.out.println( map.containsKey(key1)); // true
   System.out.println( map.containsKey(key2)); // true
   System.out.println( map.containsKey(key2clone)); // true

   key2.add( "mutate" );

   System.out.println( map.containsKey(key1)); // true
   System.out.println( map.containsKey(key2)); // false
   System.out.println( map.containsKey(key2clone)); // false (*)

   key2.remove( "mutate" );

   System.out.println( map.containsKey(key1)); // true
   System.out.println( map.containsKey(key2)); // true
   System.out.println( map.containsKey(key2clone)); // true

After key2 is mutated, the map does not contain it anymore. We could think that the map "indexes" the data when it's added and we would then expect that it still contains the key2 clone (line marked with *). But funny enough, this is not the case.

So, as the java doc says, keys should not be mutated otherwise the behavior is unspecified. Period.

I guess that's what happens in your case.

like image 181
ewernli Avatar answered Oct 08 '22 15:10

ewernli