- Type Parameters:
V- the type of the values in the map The type of the values in theMap
A specialisation of the Map interface, aimed at providing natural
default values for groups of Java types via a shared supertype. The keys in a type
map are always Class objects. The type of the values is user-defined. One
particularly useful application of type maps is to associate types with functions
(i.e. lambdas, method references) that operate on those types.
Behaviour
A TypeMap behaves as follows: if the type requested via
get() is not present in the map, but one of its supertypes
is, then the type map will return the value associated with the supertype.
Similarly, Map.containsKey(Object) containsKey()} will return true if
either the specified type itself or one of its supertypes is a key in the
TypeMap. For example: if the user requests the value associated with key
Integer.class, but the map only contains an entry for
Number.class, then the value associated with Number.class is
returned. If there is no entry for Number.class either, but there is one
for Serializable.class, then the value associated with that type is
returned.
Regular types take precedence over interface types. If the requested type is
an implementation of one type within the type map, and a subclass of another, then
the value associated with the latter will be returned. The one exception is
Object.class, since any instance of any interface is also an
Object. If Object.class is present in the map, the get()
method is guaranteed to return a non-null value. Note that this is, in fact, a
deviation from Java's type hierarchy since primitive types do not extend
Object.class. However, the point of the TypeMap interface is to
provide natural default values for groups of types, and Object.class is
the obvious candidate for providing the ultimate, last-resort, fall-back value.
Type maps are immutable. All map-altering methods throw an
UnsupportedOperationException. The
getOrDefault() method also throws an
UnsupportedOperationException as it sidesteps the TypeMap
paradigm. Type maps are also null-repellent — neither keys nor values are
allowed to be null.
You cannot instantiate a TypeMap directly. You obtain an instance
through the various static factory methods on the TypeMap interface
itself.
Autoboxing
A TypeMap can be configured to "autobox" the types requested via
get() and containsKey(). For example, if the user makes a request
for double.class, but the map only contains an entry for
Double.class, then the value associated with Double.class is
returned. If there is no entry for Double.class either, but there is one
for Number.class, then the value associated with Number.class is
returned. Thus, with autoboxing enabled, you need (and should) only add the
wrapper types to the map, unless you want the primitive type to be associated with
a different value than the wrapper type. This applies not just to primitive types,
but also to arrays of a primitive type. Thus, with autoboxing enabled,
int[] will be "autoboxed" to Integer[]. Note that, irrespective of
whether autoboxing is enabled or not, the presence of Object.class in the
map always guarantees that a non-null value will be returned for whatever
type is requested, even if it is a primitive type. Autoboxing is enabled by
default.
Auto-expansion
Even though type maps are specified to be immutable to the outside world, the
"greedy" TypeMap will automatically and
tacitly absorb subtypes of the original types in the map, as and when they are
requested via get() or containsKey(). It will look up the value
for the nearest supertype and associate the subtype with that same value. Thus,
the next time the subtype is requested, it will result in a direct hit. Note that
an auto-expanding type map is still immutable to the outside world and that the
map will still only ever contain subtypes of the types with which the map was
seeded. No new branches of the Java type hierarchy will emerge - unless, of
course, the original map contained Object.class.
Implementations
Through its static factory methods and Builder classes, the
TypeMap interface provides access to four different implementations. Which
implementation to choose strongly depends on the internal makeup of the map (the
interdependencies between the types within the map), the size of the map, and the
ratio between the size of the map and the total number of types it is going to be
queried for. For high ratios (a small map processing a large variety of types),
the "type graph" is most likely the best
choice, especially if the types in the map tend to be base types (like
Number and CharSequence) while the types requested from it are
concrete types (like Integer and String). Otherwise any of the
other implementations will do, and you will have to test which implementation
performs best.
- Author:
- Ayco Holleman
-
Nested Class Summary
-
Field Summary
Fields -
Method Summary
Modifier and TypeMethodDescriptionstatic <V> TypeMap<V>fixedTypeMap(Map<Class<?>, V> m) Returns aTypeMapthat is internally backed by a regular, unmodifiableMap.static <V> TypeMap<V>fixedTypeMap(Map<Class<?>, V> m, boolean autobox) Returns aTypeMapthat is internally backed by a regular, unmodifiableMap.static <V> TypeMapBuilder<V>Returns aBuilderfor "fixed" type maps.default VgetOrDefault(Object key, V defaultValue) Throws an UnsupportedOperationException.static <V> TypeMap<V>greedyTypeMap(Map<Class<?>, V> m) Returns an auto-expandingTypeMap.static <V> TypeMap<V>greedyTypeMap(Map<Class<?>, V> m, boolean autobox) Returns an auto-expandingTypeMap.static <V> TypeMapBuilder<V>Returns aBuilderfor "greedy" type maps.static <V> TypeMap<V>nativeTypeMap(Map<Class<?>, V> m) Returns aTypeMapthat directly implements theMapinterface rather than being backed by a regular map.static <V> TypeMap<V>nativeTypeMap(Map<Class<?>, V> m, boolean autobox) Returns aTypeMapthat directly implements theMapinterface rather than being backed by a regular map.static <V> TypeMapBuilder<V>Returns aBuilderfor "native" type maps.static <V> TypeMap<V>treeTypeMap(Map<Class<?>, V> m) Returns aTypeMapthat is backed by aTreeMap.static <V> TypeMap<V>treeTypeMap(Map<Class<?>, V> m, boolean autobox) Returns aTypeMapthat is backed by aTreeMap.static <V> TypeMapBuilder<V>Returns aBuilderfor "tree" type maps.Methods inherited from interface java.util.Map
clear, compute, computeIfAbsent, computeIfPresent, containsKey, containsValue, entrySet, equals, forEach, get, hashCode, isEmpty, keySet, merge, put, putAll, putIfAbsent, remove, remove, replace, replace, replaceAll, size, values
-
Field Details
-
SOURCE_MAP
- See Also:
-
-
Method Details
-
fixedTypeMap
Returns aTypeMapthat is internally backed by a regular, unmodifiableMap. Autoboxing is enabled in the returnedTypeMap.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMap- Returns:
- a
TypeMapthat performs reliably well in many cases
-
fixedTypeMap
Returns aTypeMapthat is internally backed by a regular, unmodifiableMap.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMapautobox- whether to "autobox" the types requested viaget()andcontainsKey()- Returns:
- a
TypeMapthat performs reliably well in many cases
-
fixedTypeMapBuilder
Returns aBuilderfor "fixed" type maps.- Type Parameters:
V- the type of the values in the map- Returns:
- a
Builderfor "fixed" type maps
-
nativeTypeMap
Returns aTypeMapthat directly implements theMapinterface rather than being backed by a regular map. Instead, it relies on a data structure similar to the Java type hierarchy itself. This implementation is sensitive to the insertion order of the types. Thus, if you expect a lot of requests for, say,String.class, it pays to initialize it with aLinkedHashMapwhereString.classwas inserted first. The keys of the returnedTypeMapare sorted from more abstract to less abstract. If present,Object.classwill be the first type in the key set. Autoboxing is enabled in the returnedTypeMap.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMap- Returns:
- a
TypeMapthat relies on a data structure similar to the Java type hierarchy itself - See Also:
-
nativeTypeMap
Returns aTypeMapthat directly implements theMapinterface rather than being backed by a regular map. Instead, it relies on a data structure similar to the Java type hierarchy itself. This implementation is sensitive to the insertion order of the types. Thus, if you expect a lot of requests for, say,String.class, it pays to initialize it with aLinkedHashMapwhereString.classwas inserted first. The keys of the returnedTypeMapare sorted from more abstract to less abstract. If present,Object.classwill be the first type in the key set.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMapautobox- whether to "autobox" the types requested viaget()andcontainsKey()- Returns:
- a
TypeMapthat relies on a data structure similar to the Java type hierarchy itself - See Also:
-
nativeTypeMapBuilder
Returns aBuilderfor "native" type maps.- Type Parameters:
V- the type of the values in the map- Returns:
- a builder for "native" type maps
-
greedyTypeMap
- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMap- Returns:
- an auto-expanding
TypeMap
-
greedyTypeMap
Returns an auto-expandingTypeMap.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMapautobox- whether to "autobox" the types requested viaget()andcontainsKey()- Returns:
- an auto-expanding
TypeMap
-
greedyTypeMapBuilder
Returns aBuilderfor "greedy" type maps.- Type Parameters:
V- the type of the values in the map- Returns:
- a
Builderfor "greedy" type maps
-
treeTypeMap
Returns aTypeMapthat is backed by aTreeMap. Its keys are sorted such that for any two types in the key set, the one that comes first will never be a supertype of the one that comes second - and vice versa: the one that comes second will never be a subtype of the one that comes first. In other words, they are sorted from less abstract to more abstract. If the map contains keyObject.class, it will be the last element in the key set. TheComparatorused for theTreeMapis similar to the one used forTypeSet.prettySort(), but much more light-weight, and therefore more performant. Autoboxing is enabled in the returnedTypeMap.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMap- Returns:
- a
TypeMapthat is backed by aTreeMap - See Also:
-
treeTypeMap
Returns aTypeMapthat is backed by aTreeMap. Its keys are sorted such that for any two types in the key set, the one that comes first will never be a supertype of the one that comes second - and vice versa: the one that comes second will never be a subtype of the one that comes first. In other words, they are sorted from less abstract to more abstract. If the map contains keyObject.class, it will be the last element in the key set. TheComparatorused for theTreeMapis similar to the one used forTypeSet.prettySort(), but more light-weight, and therefore more performant.- Type Parameters:
V- the type of the values in the map- Parameters:
m- the map to convert to aTypeMapautobox- whether to "autobox" the types requested viaget()andcontainsKey()- Returns:
- a
TypeMapthat is backed by aTreeMap - See Also:
-
treeTypeMapBuilder
Returns aBuilderfor "tree" type maps.- Type Parameters:
V- the type of the values in the map- Returns:
- a
Builderfor "tree" type maps
-
getOrDefault
Throws an UnsupportedOperationException.- Specified by:
getOrDefaultin interfaceMap<Class<?>,V>
-