Consider the following:
Map<Class<?>, Object> myMap = new HashMap<Class<?>, Object>();
Foo fooObject = New Foo();
myMap.put(fooObject.getClass(), fooObject)
Notice that java.lang.Class does not implement the hashCode() method itself, but inherits it from java.lang.Object implicitly. I verified this in JDK 1.8.
Is java.lang.Class
safe to use as a key for a java.util.HashMap
?
Will myMap.get(Foo.class)
always return the values which I put like myMap.put(fooObject.getClass(), fooObject)
? Consider the software to have various classloaders and serialization mechanisms. Will it still be the same result? If not... What would be an alternative?
We can conclude that to use a custom class for a key, it is necessary that hashCode() and equals() are implemented correctly. To put it simply, we have to ensure that the hashCode() method returns: the same value for the object as long as the state doesn't change (Internal Consistency)
If key's hash code changes after the key-value pair (Entry) is stored in HashMap, the map will not be able to retrieve the Entry. Key's hashcode can change if the key object is mutable. Mutable keys in HahsMap can result in data loss.
String is as a key of the HashMap.
HashMap's Bottleneck Because non-equal objects can have the same hash codes (a phenomenon called hash code collision), buckets can grow in size. The bucket is actually a simple linked list. Finding elements in the linked list isn't very fast (O(n)) but that's not a problem if the list is very small.
Off the top of my head, would there be any reason to just not use the string class names? E.g. instead use:
myMap.put("Foo", fooObject);
If you are paranoid that maybe there could be more than one Foo
class in scope, you could use the full canonical name:
myMap.put(Foo.class.getCanonicalName(), fooObject);
Is java.lang.Class safe to use as a key for a java.util.HashMap?
Yes.
Will myMap.get(Foo.class) always return the values which I put like myMap.put(fooObject.getClass(), fooObject)?
Yes.
Using a Class
object as a key in a HashMap
is safe. The Class
class inherits the Object::equals
and Object::hashCode
methods. Thus equals
for Class
objects is testing object identity.
This is the correct semantics for type equality in Java. The implementation of the ClassLoader::defineClass
method ensures that you can never get two different Class
objects representing the same Java type.
However, there is a wrinkle. The Java Language specification (JLS 4.3.4) states this:
At run time, several reference types with the same binary name may be loaded simultaneously by different class loaders. These types may or may not represent the same type declaration. Even if two such types do represent the same type declaration, they are considered distinct.
(The binary name is related to the FQDN of a named type, and takes account of anonymous classes, and array types.)
What this means is that if you (successfully) call ClassLoader::defineClass
for classes with the same fully qualified name in two different classloaders, you will get different Java types. Irrespective of the bytecodes you used. Furthermore, if you attempt to cast from one type to the other one, you will get a class cast exception.
Now the question is does this matter in your use-case?
Answer: probably not.
Unless you (or your framework) are doing tricky things with classloaders, the situation does not arise.
If it does, then you probably need the two types (with the same FQDN and different classloaders) to have the different entries in the HashMap
. (Because the types are different!)
But if you need the two types to have the same entry, then you can use the FQDN for the class as the key, which you can obtain using Class::getCanonicalName
. If you need to cope with array classes, etc, then use Class::getName
which returns the binary name for the type.
What about serialization mechanisms?
A Class
object cannot be serialized using Object serialization, since Class
does not implement Serializable
. If you implement / use some other serialization mechanism that does support the serialization of Class
objects, then that mechanism needs to be compatible with JLS 4.3.4.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With