Class MultiKeyMap<V>
- Type Parameters:
V
- the type of values stored in the map
- All Implemented Interfaces:
ConcurrentMap<Object,
,V> Map<Object,
V>
MultiKeyMap allows storing and retrieving values using multiple keys. Unlike traditional maps that use a single key, this map can handle keys with any number of components, making it ideal for complex lookup scenarios like user permissions, configuration trees, and caching systems.
Key Features:
- N-Dimensional Keys: Support for keys with any number of components (1, 2, 3, ... N).
- High Performance: Zero-allocation polymorphic storage, polynomial rolling hash, and optimized hash computation — no GC/heap pressure for gets in flat cases.
- Thread-Safe: Lock-free reads with auto-tuned stripe locking that scales with your server cores, similar to ConcurrentHashMap.
- Map Interface Compatible: Supports single-key operations via the standard Map interface (get()/put() automatically unpack Collections/Arrays into multi-keys).
- Flexible API: Var-args methods for convenient multi-key operations (getMultiKey()/putMultiKey() with many keys).
- Smart Collection Handling: Configurable behavior for Collections via
MultiKeyMap.CollectionKeyMode
— change the default automatic unpacking capability as needed. - N-Dimensional Array Expansion: Nested arrays of any depth are automatically flattened recursively into multi-keys.
- Cross-Container Equivalence: Arrays and Collections with equivalent structure are treated as identical keys, regardless of container type.
Dimensional Behavior Control:
MultiKeyMap provides revolutionary control over how dimensions are handled through the flattenDimensions
parameter:
- Structure-Preserving Mode (default, flattenDimensions = false): Different structural depths remain distinct keys. Arrays/Collections with different nesting levels create separate entries.
- Dimension-Flattening Mode (flattenDimensions = true): All equivalent flat representations are treated as identical keys, regardless of original container structure.
Performance Characteristics:
- Lock-Free Reads: Get operations require no locking for optimal concurrent performance
- Auto-Tuned Stripe Locking: Write operations use stripe locking that adapts to your server's core count
- Zero-Allocation Gets: No temporary objects created during retrieval operations
- Polymorphic Storage: Efficient memory usage adapts storage format based on key complexity
- Simple Keys Mode: Optional performance optimization that skips nested structure checks when keys are known to be flat
Value-Based vs Type-Based Equality:
MultiKeyMap provides two equality modes for key comparison, controlled via the valueBasedEquality
parameter:
- Value-Based Equality (default, valueBasedEquality = true): Cross-type numeric comparisons work naturally. Integer 1 equals Long 1L equals Double 1.0. This mode is ideal for configuration lookups and user-friendly APIs.
- Type-Based Equality (valueBasedEquality = false): Strict type checking - Integer 1 ≠ Long 1L. This mode provides traditional Java Map semantics and maximum performance.
Value-Based Equality Edge Cases:
- NaN Behavior: In value-based mode,
NaN == NaN
returns true (unlike Java's default). This ensures consistent key lookups with floating-point values. - Zero Handling:
+0.0 == -0.0
returns true in both modes (standard Java behavior). - BigDecimal Precision: Doubles are converted via
new BigDecimal(number.toString())
. This means0.1d
equalsBigDecimal("0.1")
but NOTBigDecimal(0.1)
(the latter has binary rounding errors). - Infinity Handling: Comparing
Double.POSITIVE_INFINITY
orNEGATIVE_INFINITY
to BigDecimal returns false (BigDecimal cannot represent infinity). - Atomic Types: In type-based mode, only identical atomic types match (AtomicInteger ≠ Integer). In value-based mode, atomic types participate in numeric families (AtomicInteger(1) == Integer(1)).
Case Sensitivity for CharSequences:
MultiKeyMap provides configurable case sensitivity for CharSequence keys (String, StringBuilder, etc.),
controlled via the caseSensitive
parameter:
- Case-Sensitive Mode (default, caseSensitive = true): CharSequences are compared using their standard equals() methods. "Hello" and "hello" are different keys.
- Case-Insensitive Mode (caseSensitive = false): All CharSequence instances are compared case-insensitively. "Hello", "HELLO", and "hello" are treated as the same key.
API Overview:
MultiKeyMap provides two complementary APIs:
- Map Interface: Use as
Map<Object, V>
for compatibility with existing code and single-key operations - MultiKeyMap API: Declare as
MultiKeyMap<V>
to access powerful var-args methods for multidimensional operations
Usage Examples:
// Basic multi-dimensional usage
MultiKeyMap<String> map = new MultiKeyMap<>();
map.putMultiKey("user-config", "user123", "settings", "theme");
String theme = map.getMultiKey("user123", "settings", "theme");
// Cross-container equivalence
map.put(new String[]{"key1", "key2"}, "value1"); // Array key
String value = map.get(Arrays.asList("key1", "key2")); // Collection lookup - same key!
// Structure-preserving vs flattening modes
MultiKeyMap<String> structured = MultiKeyMap.<String>builder().flattenDimensions(false).build(); // Structure-preserving (default)
MultiKeyMap<String> flattened = MultiKeyMap.<String>builder().flattenDimensions(true).build(); // Dimension-flattening
// Performance optimization for flat keys (no nested arrays/collections)
MultiKeyMap<String> fast = MultiKeyMap.<String>builder()
.simpleKeysMode(true) // Skip nested structure checks for maximum performance
.capacity(50000) // Pre-size for known data volume
.build();
// Value-based vs Type-based equality
MultiKeyMap<String> valueMap = MultiKeyMap.<String>builder().valueBasedEquality(true).build(); // Default
valueMap.putMultiKey("found", 1, 2L, 3.0); // Mixed numeric types
String result = valueMap.getMultiKey(1L, 2, 3); // Found! Cross-type numeric matching
MultiKeyMap<String> typeMap = MultiKeyMap.<String>builder().valueBasedEquality(false).build();
typeMap.putMultiKey("int-key", 1, 2, 3);
String missing = typeMap.getMultiKey(1L, 2L, 3L); // null - different types don't match
// Case-insensitive string keys
MultiKeyMap<String> caseInsensitive = MultiKeyMap.<String>builder().caseSensitive(false).build();
caseInsensitive.putMultiKey("value", "USER", "Settings", "THEME");
String found = caseInsensitive.getMultiKey("user", "settings", "theme"); // Found! Case doesn't matter
For comprehensive examples and advanced usage patterns, see the user guide documentation.
- Author:
- John DeRegnaucourt ([email protected])
Copyright (c) Cedar Software LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
License
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic class
Builder for creating configured MultiKeyMap instances.static enum
Controls how Collections are treated when used as keys in MultiKeyMap.static class
-
Constructor Summary
ConstructorsConstructorDescriptionMultiKeyMap
(int capacity) MultiKeyMap
(int capacity, float loadFactor) MultiKeyMap
(MultiKeyMap<? extends V> source) -
Method Summary
Modifier and TypeMethodDescriptionstatic <V> MultiKeyMap.Builder<V>
builder()
void
clear()
Removes all the mappings from this map.Attempts to compute a mapping for the specified key and its current mapped value (ornull
if there is no current mapping).computeIfAbsent
(Object key, Function<? super Object, ? extends V> mappingFunction) If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unlessnull
.computeIfPresent
(Object key, BiFunction<? super Object, ? super V, ? extends V> remappingFunction) If the specified key is not already associated with a value, attempts to compute a new mapping given the key and its current mapped value.boolean
containsKey
(Object key) Returnstrue
if this map contains a mapping for the specified key.boolean
containsMultiKey
(Object... keys) Returnstrue
if this map contains a mapping for the specified multidimensional key using var-args syntax.boolean
containsValue
(Object value) Returnstrue
if this map maps one or more keys to the specified value.entries()
Returns anIterable
ofMultiKeyMap.MultiKeyEntry
objects representing all key-value mappings in this map.entrySet()
Returns aSet
view of the mappings contained in this map.boolean
Compares the specified object with this map for equality.Returns the value to which the specified key is mapped, ornull
if this map contains no mapping for the key.boolean
Returns the current case sensitivity setting for CharSequence comparisons.Returns the current collection key mode setting.boolean
Returns the current dimension flattening setting.getMultiKey
(Object... keys) Retrieves the value associated with the specified multidimensional key using var-args syntax.boolean
Returns the current simple keys mode setting.int
hashCode()
Returns the hash code value for this map.boolean
isEmpty()
Returnstrue
if this map contains no key-value mappings.keySet()
Returns aSet
view of the keys contained in this map.If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value.void
Prints detailed contention statistics for this map's stripe locking system to the logger.Associates the specified value with the specified key in this map.void
Copies all the mappings from the specified map to this map.putIfAbsent
(Object key, V value) If the specified key is not already associated with a value, associates it with the given value.putMultiKey
(V value, Object... keys) Associates the specified value with the specified multidimensional key using var-args syntax.Removes the mapping for the specified key from this map if it is present.boolean
Removes the entry for a key only if it is currently mapped to the specified value.removeMultiKey
(Object... keys) Removes the mapping for the specified multidimensional key using var-args syntax.Replaces the entry for the specified key only if it is currently mapped to some value.boolean
Replaces the entry for the specified key only if currently mapped to the specified value.int
size()
Returns the number of key-value mappings in this map.toString()
Returns a string representation of this map.values()
Returns aCollection
view of the values contained in this map.Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
Methods inherited from interface java.util.concurrent.ConcurrentMap
forEach, getOrDefault, replaceAll
-
Constructor Details
-
MultiKeyMap
-
MultiKeyMap
public MultiKeyMap() -
MultiKeyMap
public MultiKeyMap(int capacity) -
MultiKeyMap
public MultiKeyMap(int capacity, float loadFactor)
-
-
Method Details
-
builder
-
getCollectionKeyMode
Returns the current collection key mode setting.This mode determines how Collections are treated when used as keys in this map.
- Returns:
- the current
MultiKeyMap.CollectionKeyMode
- either COLLECTIONS_EXPANDED (default) where Collections are automatically unpacked into multi-key entries, or COLLECTIONS_NOT_EXPANDED where Collections are treated as single key objects - See Also:
-
getFlattenDimensions
public boolean getFlattenDimensions()Returns the current dimension flattening setting.This setting controls how nested arrays and collections are handled when used as keys.
- Returns:
true
if dimension flattening is enabled (all equivalent flat representations are treated as identical keys regardless of original container structure),false
if structure-preserving mode is used (default, where different structural depths remain distinct keys)
-
getSimpleKeysMode
public boolean getSimpleKeysMode()Returns the current simple keys mode setting.This performance optimization setting indicates whether the map assumes keys do not contain nested arrays or collections.
- Returns:
true
if simple keys mode is enabled (nested structure checks are skipped for maximum performance),false
if normal operation with full nested structure support
-
getCaseSensitive
public boolean getCaseSensitive()Returns the current case sensitivity setting for CharSequence comparisons.This setting controls how CharSequence instances (String, StringBuilder, etc.) are compared within keys.
- Returns:
true
if case-sensitive comparison is enabled (default),false
if case-insensitive comparison is used- Since:
- 3.6.0
-
getMultiKey
Retrieves the value associated with the specified multidimensional key using var-args syntax.This is a convenience method that allows easy multi-key lookups without having to pass arrays or collections. The keys are treated as separate dimensions of a multi-key.
- Parameters:
keys
- the key components to look up. Can be null or empty (treated as null key), single key, or multiple key components- Returns:
- the value associated with the multi-key, or
null
if no mapping exists - See Also:
-
get
Returns the value to which the specified key is mapped, ornull
if this map contains no mapping for the key.This method supports both single keys and multidimensional keys. Arrays and Collections are automatically expanded into multi-keys based on the map's configuration settings.
-
putMultiKey
Associates the specified value with the specified multidimensional key using var-args syntax.This is a convenience method that allows easy multi-key storage without having to pass arrays or collections. The keys are treated as separate dimensions of a multi-key.
- Parameters:
value
- the value to be associated with the multi-keykeys
- the key components for the mapping. Can be null or empty (treated as null key), single key, or multiple key components- Returns:
- the previous value associated with the multi-key, or
null
if there was no mapping for the key - See Also:
-
put
Associates the specified value with the specified key in this map.This method supports both single keys and multidimensional keys. Arrays and Collections are automatically expanded into multi-keys based on the map's configuration settings.
- Specified by:
put
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associated. Can be a single object, array, or Collection that will be normalized according to the map's settingsvalue
- the value to be associated with the specified key- Returns:
- the previous value associated with the key, or
null
if there was no mapping for the key
-
containsMultiKey
Returnstrue
if this map contains a mapping for the specified multidimensional key using var-args syntax.This is a convenience method that allows easy multi-key existence checks without having to pass arrays or collections. The keys are treated as separate dimensions of a multi-key.
- Parameters:
keys
- the key components to check for. Can be null or empty (treated as null key), single key, or multiple key components- Returns:
true
if this map contains a mapping for the specified multi-key- See Also:
-
containsKey
Returnstrue
if this map contains a mapping for the specified key.This method supports both single keys and multidimensional keys. Arrays and Collections are automatically expanded into multi-keys based on the map's configuration settings.
- Specified by:
containsKey
in interfaceMap<Object,
V> - Parameters:
key
- the key whose presence in this map is to be tested. Can be a single object, array, or Collection that will be normalized according to the map's settings- Returns:
true
if this map contains a mapping for the specified key
-
removeMultiKey
Removes the mapping for the specified multidimensional key using var-args syntax.This is a convenience method that allows easy multi-key removal without having to pass arrays or collections. The keys are treated as separate dimensions of a multi-key.
- Parameters:
keys
- the key components for the mapping to remove. Can be null or empty (treated as null key), single key, or multiple key components- Returns:
- the previous value associated with the multi-key, or
null
if there was no mapping for the key - See Also:
-
remove
Removes the mapping for the specified key from this map if it is present.This method supports both single keys and multidimensional keys. Arrays and Collections are automatically expanded into multi-keys based on the map's configuration settings.
- Specified by:
remove
in interfaceMap<Object,
V> - Parameters:
key
- the key whose mapping is to be removed from the map. Can be a single object, array, or Collection that will be normalized according to the map's settings- Returns:
- the previous value associated with the key, or
null
if there was no mapping for the key
-
size
public int size()Returns the number of key-value mappings in this map. -
isEmpty
public boolean isEmpty()Returnstrue
if this map contains no key-value mappings. -
clear
public void clear()Removes all the mappings from this map. The map will be empty after this call returns. -
containsValue
Returnstrue
if this map maps one or more keys to the specified value.This operation requires time linear in the map size.
- Specified by:
containsValue
in interfaceMap<Object,
V> - Parameters:
value
- the value whose presence in this map is to be tested- Returns:
true
if this map maps one or more keys to the specified value
-
keySet
Returns aSet
view of the keys contained in this map.Multidimensional keys are represented as immutable List
-
values
Returns aCollection
view of the values contained in this map.Changes to the returned collection are not reflected in the map.
-
entrySet
Returns aSet
view of the mappings contained in this map.Multidimensional keys are represented as immutable List
-
putAll
Copies all the mappings from the specified map to this map.The effect of this call is equivalent to that of calling
put(Object, Object)
on this map once for each mapping from keyk
to valuev
in the specified map.- Specified by:
putAll
in interfaceMap<Object,
V> - Parameters:
m
- mappings to be stored in this map- Throws:
NullPointerException
- if the specified map is null
-
putIfAbsent
If the specified key is not already associated with a value, associates it with the given value.This is equivalent to:
if (!map.containsKey(key)) return map.put(key, value); else return map.get(key);
- Specified by:
putIfAbsent
in interfaceConcurrentMap<Object,
V> - Specified by:
putIfAbsent
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedvalue
- the value to be associated with the specified key- Returns:
- the previous value associated with the specified key, or
null
if there was no mapping for the key
-
computeIfAbsent
If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unlessnull
.The entire method invocation is performed atomically, so the function is applied at most once per key.
- Specified by:
computeIfAbsent
in interfaceConcurrentMap<Object,
V> - Specified by:
computeIfAbsent
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedmappingFunction
- the function to compute a value- Returns:
- the current (existing or computed) value associated with the specified key,
or
null
if the computed value isnull
- Throws:
NullPointerException
- if the specified mappingFunction is null
-
computeIfPresent
public V computeIfPresent(Object key, BiFunction<? super Object, ? super V, ? extends V> remappingFunction) If the specified key is not already associated with a value, attempts to compute a new mapping given the key and its current mapped value.The entire method invocation is performed atomically. If the function returns
null
, the mapping is removed.- Specified by:
computeIfPresent
in interfaceConcurrentMap<Object,
V> - Specified by:
computeIfPresent
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedremappingFunction
- the function to compute a value- Returns:
- the new value associated with the specified key, or
null
if none - Throws:
NullPointerException
- if the specified remappingFunction is null
-
compute
Attempts to compute a mapping for the specified key and its current mapped value (ornull
if there is no current mapping).The entire method invocation is performed atomically. If the function returns
null
, the mapping is removed (or remains absent if initially absent).- Specified by:
compute
in interfaceConcurrentMap<Object,
V> - Specified by:
compute
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedremappingFunction
- the function to compute a value- Returns:
- the new value associated with the specified key, or
null
if none - Throws:
NullPointerException
- if the specified remappingFunction is null
-
merge
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result isnull
.The entire method invocation is performed atomically.
- Specified by:
merge
in interfaceConcurrentMap<Object,
V> - Specified by:
merge
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the resulting value is to be associatedvalue
- the non-null value to be merged with the existing valueremappingFunction
- the function to recompute a value if present- Returns:
- the new value associated with the specified key, or
null
if no value is associated with the key - Throws:
NullPointerException
- if the specified value or remappingFunction is null
-
remove
Removes the entry for a key only if it is currently mapped to the specified value.This is equivalent to:
if (map.containsKey(key) && Objects.equals(map.get(key), value)) { map.remove(key); return true; } else return false;
-
replace
Replaces the entry for the specified key only if it is currently mapped to some value.This is equivalent to:
if (map.containsKey(key)) { return map.put(key, value); } else return null;
- Specified by:
replace
in interfaceConcurrentMap<Object,
V> - Specified by:
replace
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedvalue
- the value to be associated with the specified key- Returns:
- the previous value associated with the specified key, or
null
if there was no mapping for the key
-
replace
Replaces the entry for the specified key only if currently mapped to the specified value.This is equivalent to:
if (map.containsKey(key) && Objects.equals(map.get(key), oldValue)) { map.put(key, newValue); return true; } else return false;
- Specified by:
replace
in interfaceConcurrentMap<Object,
V> - Specified by:
replace
in interfaceMap<Object,
V> - Parameters:
key
- the key with which the specified value is to be associatedoldValue
- the value expected to be associated with the specified keynewValue
- the value to be associated with the specified key- Returns:
true
if the value was replaced
-
hashCode
public int hashCode()Returns the hash code value for this map.The hash code of a map is defined to be the sum of the hash codes of each entry in the map's
entrySet()
view. This ensures thatm1.equals(m2)
implies thatm1.hashCode()==m2.hashCode()
for any two mapsm1
andm2
, as required by the general contract ofObject.hashCode()
. -
equals
Compares the specified object with this map for equality.Returns
true
if the given object is also a map and the two maps represent the same mappings. Two mapsm1
andm2
represent the same mappings ifm1.entrySet().equals(m2.entrySet())
. -
toString
Returns a string representation of this map.The string representation consists of a list of key-value mappings in the order returned by the map's entries iterator, enclosed in braces ({}).
Each key-value mapping is rendered as "key → value", where the key part shows all key components and the value part shows the mapped value. Adjacent mappings are separated by commas and newlines.
Empty maps are represented as "{}".
-
entries
Returns anIterable
ofMultiKeyMap.MultiKeyEntry
objects representing all key-value mappings in this map.Each
MultiKeyEntry
contains the complete key information as an Object array and the associated value. This provides access to the full multidimensional key structure that may not be available through the standardentrySet()
method.The returned iterable provides a weakly consistent view - it captures the buckets reference at creation time and walks live bucket elements. Concurrent modifications may or may not be reflected during iteration, and the iterator will never throw ConcurrentModificationException.
- Returns:
- an iterable of
MultiKeyEntry
objects containing all mappings in this map - See Also:
-
printContentionStatistics
public void printContentionStatistics()Prints detailed contention statistics for this map's stripe locking system to the logger.This method outputs comprehensive performance monitoring information including:
- Total lock acquisitions and contentions across all operations
- Global lock statistics (used during resize operations)
- Per-stripe breakdown showing acquisitions, contentions, and contention rates
- Analysis of stripe distribution including most/least contended stripes
- Count of unused stripes for load balancing assessment
This information is useful for performance tuning and understanding concurrency patterns in high-throughput scenarios. The statistics are logged at INFO level.
- See Also:
-
STRIPE_COUNT
-