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.
Key Features:
- N-Dimensional Keys: Support for keys with any number of components (1, 2, 3, ... N).
- High Performance: Zero-allocation polymorphic storage and optimized hash computation — no GC/heap used for "gets".
- Thread-Safe: Lock-free reads with auto-tuned stripe locking that scales with your server, similar to ConcurrentHashMap.
- Map Interface Compatible: Supports single-key operations via the standard Map interface (get()/put() automatically unpack Collections/Arrays (typed or Object[]) 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 — change the default automatic unpacking capability as needed.
- N-Dimensional Array Expansion: Nested arrays of any depth are automatically flattened recursively into multi-keys.
Usage Examples:
// Create a multi-key map
MultiKeyMap<String> map = new MultiKeyMap<>(1024);
// Store values with different key dimensions using Map interface
map.put("single-key", "value1"); // 1D key
map.put(new Object[]{"k1", "k2"}, "value2"); // 2D key via array
map.put(Arrays.asList("k1", "k2", "k3"), "value3"); // 3D key via Collection
// OR use convenient varargs methods (requires MultiKeyMap variable type)
MultiKeyMap<String> mkMap = new MultiKeyMap<>();
mkMap.putMultiKey("value1", "single-key"); // 1D key
mkMap.putMultiKey("value2", "key1", "key2"); // 2D key
mkMap.putMultiKey("value3", "key1", "key2", "key3"); // 3D key
mkMap.putMultiKey("value4", "k1", "k2", "k3", "k4"); // 4D key
// ... unlimited dimensions
// Retrieve values using matching signatures
String val1 = map.get("single-key");
String val2 = map.get(new Object[]{"k1", "k2"});
String val3 = mkMap.getMultiKey("key1", "key2");
String val4 = mkMap.getMultiKey("k1", "k2", "k3", "k4");
N-Dimensional Array Expansion:
MultiKeyMap automatically expands nested arrays of any depth into their constituent elements, providing powerful key flattening capabilities:
MultiKeyMap<String> map = new MultiKeyMap<>();
// 🔥 N-DIMENSIONAL ARRAY EXPANSION - The Ultimate Power Feature
// Arrays and Collections expand with structure preservation - no limits on depth!
// ✅ EQUIVALENT (same flat structure, cross-container support):
String[] flatArray = {"config", "database", "url"}; // → ["config", "database", "url"]
List<String> flatList = List.of("config", "database", "url"); // → ["config", "database", "url"]
map.put(flatArray, "connection-string");
// ALL of these work - cross-container equivalence:
String result1 = map.get(flatArray); // ✅ Original String[]
String result2 = map.get(flatList); // ✅ Equivalent List
String result3 = map.getMultiKey("config", "database", "url"); // ✅ Individual elements
// ❌ NOT EQUIVALENT (different structures preserved):
String[][] nested2D = {{"config", "database"}, {"url", "port"}}; // → [🔒, ⬇, "config", "database", ⬆, ⬇, "url", "port", ⬆]
String[] flat1D = {"config", "database", "url", "port"}; // → ["config", "database", "url", "port"]
map.put(nested2D, "2D-structure"); // Stored with sentinels
map.put(flat1D, "flat-structure"); // Stored without sentinels
// These create SEPARATE entries:
String val1 = map.get(nested2D); // ✅ "2D-structure" (uses original array)
String val2 = map.get(flat1D); // ✅ "flat-structure" (uses original array)
// Note: getMultiKey("config", "database", "url", "port") only retrieves flat1D
Collection and Array Handling:
MultiKeyMap provides flexible handling of Collections through the
MultiKeyMap.CollectionKeyMode
enum:
- COLLECTIONS_EXPANDED: Collections are always unpacked into multi-key lookups (default)
- COLLECTIONS_NOT_EXPANDED: Collections tried as single key first, fallback to unpacking
Arrays are ALWAYS expanded regardless of CollectionKeyMode setting because they lack meaningful equals()/hashCode() implementations and cannot serve as useful Map keys.
// Configure collection handling behavior (affects Collections only, not Arrays)
MultiKeyMap<String> map = new MultiKeyMap<>(1024, CollectionKeyMode.COLLECTIONS_NOT_EXPANDED);
List<String> collectionKey = Arrays.asList("config", "database", "url");
map.put(collectionKey, "jdbc:mysql://localhost:3306/db"); // Collection tried as single key first
String url = map.get(collectionKey); // Retrieved as single key
String[] arrayKey = {"config", "database", "url"};
map.put(arrayKey, "another-value"); // Array ALWAYS expanded to 3D key
String value = map.get("config", "database", "url"); // Retrieved as 3D key
Performance Characteristics:
- Time Complexity: O(1) average case for get/put/remove operations
- Space Complexity: O(n) where n is the number of stored key-value pairs
- Memory Efficiency: Polymorphic storage (Object vs Object[]) eliminates wrappers
- Concurrency: Lock-free reads with auto-tuned stripe locking that scales with your server
- Load Factor: Configurable, defaults to 0.75 for optimal performance
Thread Safety:
This implementation is fully thread-safe with enterprise-grade concurrency. Read operations (get, containsKey, etc.) are completely lock-free for maximum throughput. Write operations use auto-tuned stripe locking that scales with your server's cores, enabling multiple concurrent writers to operate simultaneously without contention. The stripe count auto-adapts to system cores (cores/2, minimum 8) for optimal performance across different hardware. Global operations (resize, clear) use coordinated locking to prevent deadlock while maintaining data consistency.
- 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 enum
Enum to control how Collections are handled in get/remove/containsKey operations.static class
Represents a single entry in the MultiKeyMap. -
Constructor Summary
ConstructorsConstructorDescriptionCreates a new MultiKeyMap with default capacity (16) and default load factor (0.75).MultiKeyMap
(boolean flattenDimensions) Creates a new MultiKeyMap with default capacity and dimension flattening.MultiKeyMap
(int capacity) Creates a new MultiKeyMap with the specified capacity and default load factor.MultiKeyMap
(int capacity, float loadFactor) Creates a new MultiKeyMap with the specified capacity and load factor.MultiKeyMap
(int capacity, float loadFactor, MultiKeyMap.CollectionKeyMode collectionKeyMode) Creates a new MultiKeyMap with specified capacity, load factor, and collection key behavior.MultiKeyMap
(int capacity, float loadFactor, MultiKeyMap.CollectionKeyMode collectionKeyMode, boolean flattenDimensions) Creates a new MultiKeyMap with specified capacity, load factor, collection key behavior, and dimension flattening.MultiKeyMap
(MultiKeyMap.CollectionKeyMode collectionKeyMode, boolean flattenDimensions) Creates a new MultiKeyMap with default capacity (16), specified collection key behavior, and dimension flattening. -
Method Summary
Modifier and TypeMethodDescriptionvoid
clear()
Removes all the mappings from this map.computeIfAbsent
(Object key, Function<? super Object, ? extends V> mappingFunction) computeIfPresent
(Object key, BiFunction<? super Object, ? super V, ? extends V> remappingFunction) boolean
containsKey
(Object key) Map interface compatible containsKey method.boolean
containsMultiKey
(Object... keys) Returns true if this map contains a mapping for the specified N-dimensional key.boolean
containsValue
(Object value) Returns true if this map maps one or more keys to the specified value.entries()
Returns an iterator over all entries in the map.entrySet()
Returns a Set view of the mappings contained in this map.boolean
Compares the specified object with this map for equality.static Object[]
expandMultiDimensionalArray
(Object sourceArrayOrCollection) Expands n-dimensional arrays and nested collections recursively into a flat Object[] array.Map interface compatible get method with zero-allocation direct storage.Returns the CollectionKeyMode used by this MultiKeyMap.boolean
Returns whether this MultiKeyMap flattens dimensions.double
Returns the current load factor.int
Returns the maximum chain length encountered so far.getMultiKey
(Object... keys) Gets the conversion function for the given N-dimensional key, or null if not found.int
hashCode()
Returns the hash code value for this map.boolean
isEmpty()
Returns true if this map contains no key-value mappings.keySet()
Returns a Set view of the keys contained in this map.void
Prints detailed contention statistics for debugging performance issues.Map interface compatible put method with auto-unpacking for arrays.void
putIfAbsent
(Object key, V value) putMultiKey
(V value, Object... keys) Premium var-args API - Store a value with unlimited multiple keys.Map interface compatible remove method with auto-unpacking for arrays and collections.boolean
removeMultiKey
(Object... keys) Removes the mapping for the specified N-dimensional key from this map if it is present.boolean
int
size()
Returns the current number of entries in the map.toString()
Returns a string representation of this map.values()
Returns a Collection 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
public MultiKeyMap(int capacity, float loadFactor) Creates a new MultiKeyMap with the specified capacity and load factor.- Parameters:
capacity
- the initial capacityloadFactor
- the load factor threshold for resizing
-
MultiKeyMap
Creates a new MultiKeyMap with specified capacity, load factor, and collection key behavior.- Parameters:
capacity
- the initial capacityloadFactor
- the load factor threshold for resizingcollectionKeyMode
- how to handle Collections/Arrays in get/remove/containsKey operations
-
MultiKeyMap
public MultiKeyMap(int capacity, float loadFactor, MultiKeyMap.CollectionKeyMode collectionKeyMode, boolean flattenDimensions) Creates a new MultiKeyMap with specified capacity, load factor, collection key behavior, and dimension flattening.- Parameters:
capacity
- the initial capacityloadFactor
- the load factor threshold for resizingcollectionKeyMode
- how to handle Collections/Arrays in get/remove/containsKey operationsflattenDimensions
- if true, flatten all dimensions (["a"] = [["a"]] = "a"); if false, preserve structure
-
MultiKeyMap
public MultiKeyMap()Creates a new MultiKeyMap with default capacity (16) and default load factor (0.75). -
MultiKeyMap
Creates a new MultiKeyMap with default capacity (16), specified collection key behavior, and dimension flattening.- Parameters:
collectionKeyMode
- how to handle Collections/Arrays in get/remove/containsKey operationsflattenDimensions
- if true, flatten all dimensions (["a"] = [["a"]] = "a"); if false, preserve structure
-
MultiKeyMap
public MultiKeyMap(int capacity) Creates a new MultiKeyMap with the specified capacity and default load factor.- Parameters:
capacity
- the initial capacity
-
MultiKeyMap
public MultiKeyMap(boolean flattenDimensions) Creates a new MultiKeyMap with default capacity and dimension flattening.- Parameters:
flattenDimensions
- if true, flatten all dimensions (["a"] = [["a"]] = "a"); if false, preserve structure
-
-
Method Details
-
getCollectionKeyMode
Returns the CollectionKeyMode used by this MultiKeyMap.- Returns:
- the CollectionKeyMode indicating how Collections are handled
-
getFlattenDimensions
public boolean getFlattenDimensions()Returns whether this MultiKeyMap flattens dimensions.- Returns:
- true if dimensions are flattened (["a"] = [["a"]] = "a"), false if structure is preserved
-
getMultiKey
Gets the conversion function for the given N-dimensional key, or null if not found. This method is lock-free for maximum read performance.- Parameters:
keys
- the key components (can be varargs or Object[])- Returns:
- the value associated with the key, or null if not found
-
get
Map interface compatible get method with zero-allocation direct storage. Supports both single keys and N-dimensional keys via Object[] detection. -
putMultiKey
Premium var-args API - Store a value with unlimited multiple keys. This is the recommended API for MultiKeyMap users as it provides the best developer experience with unlimited keys and zero array allocations for inline arguments.Examples:
MultiKeyMap<Employee> map = new MultiKeyMap<>(); // Zero allocation - no arrays created map.put(employee, "dept", "engineering", "senior"); map.put(person, "location", "building1", "floor2", "room101"); // Works with existing arrays too String[] keyArray = {"dept", "marketing", "director"}; map.put(manager, keyArray); // Passes array directly to varargs
- Parameters:
value
- the value to storekeys
- the key components (unlimited number)- Returns:
- the previous value associated with the key, or null if there was no mapping
-
put
Map interface compatible put method with auto-unpacking for arrays. This provides a great experience for Map users by automatically detecting and unpacking arrays into multi-key calls.Auto-unpacking behavior:
- If key is an array → automatically unpacked into multiple keys
- If key is a Collection → automatically unpacked into multiple keys
- Otherwise → treated as single key
Examples:
Map<Object, Employee> map = new MultiKeyMap<>(); // Auto-unpacking: array becomes multi-key String[] keys = {"dept", "engineering", "senior"}; map.put(keys, employee); // Stored as 3-key entry // Auto-unpacking: Collection becomes multi-key List<String> keyList = Arrays.asList("dept", "sales", "junior"); map.put(keyList, employee); // Stored as 3-key entry // Single key: other objects stored normally map.put("manager", boss); // Stored as single-key entry // Typed arrays also auto-unpack int[] intKeys = {1, 2, 3}; map.put(intKeys, data); // Stored as 3-key entry
-
expandMultiDimensionalArray
Expands n-dimensional arrays and nested collections recursively into a flat Object[] array. Uses iterative approach with cycle detection to avoid infinite loops and recursion limits.This utility method is available for use by other classes that need n-dimensional array/collection expansion functionality, such as CaseInsensitiveMap.
- Parameters:
sourceArrayOrCollection
- The array or collection to expand (may contain nested structures)- Returns:
- A flattened Object[] containing all elements from nested arrays/collections
-
size
public int size()Returns the current number of entries in the map. -
getMaxChainLength
public int getMaxChainLength()Returns the maximum chain length encountered so far. -
getLoadFactor
public double getLoadFactor()Returns the current load factor. -
entries
Returns an iterator over all entries in the map. The iterator captures a snapshot of the current state and is thread-safe for reads. Concurrent modifications during iteration may not be reflected in the iteration. -
isEmpty
public boolean isEmpty()Returns true if this map contains no key-value mappings. -
containsValue
Returns true if this map maps one or more keys to the specified value.- Specified by:
containsValue
in interfaceMap<Object,
V>
-
removeMultiKey
Removes the mapping for the specified N-dimensional key from this map if it is present.- Parameters:
keys
- the key components (can be varargs)- Returns:
- the previous value associated with the key, or null if there was no mapping
-
remove
Map interface compatible remove method with auto-unpacking for arrays and collections. This provides a great experience for Map users by automatically detecting and unpacking arrays/collections into multi-key calls.Auto-unpacking behavior:
- If key is an array → automatically unpacked into multiple keys
- If key is a Collection → automatically unpacked into multiple keys
- Otherwise → treated as single key
Examples:
Map<Object, Employee> map = new MultiKeyMap<>(); // Auto-unpacking: array becomes multi-key String[] keys = {"dept", "engineering", "senior"}; Employee removed = map.remove(keys); // Removes 3-key entry // Auto-unpacking: Collection becomes multi-key List<String> keyList = Arrays.asList("dept", "sales", "junior"); Employee removed2 = map.remove(keyList); // Removes 3-key entry // Single key: other objects removed normally Employee manager = map.remove("manager"); // Removes single-key entry // Typed arrays also auto-unpack int[] intKeys = {1, 2, 3}; Data removed3 = map.remove(intKeys); // Removes 3-key entry
-
putAll
-
putIfAbsent
Uses a double-check locking pattern to avoid unnecessary synchronization when a value is already present. If the key is absent or currently mapped to
null
, the provided value is stored.- Specified by:
putIfAbsent
in interfaceConcurrentMap<Object,
V> - Specified by:
putIfAbsent
in interfaceMap<Object,
V> - See Also:
-
computeIfAbsent
Performs a double-check locking pattern to avoid unnecessary synchronization when the value already exists. If the value is absent or
null
, the mapping function is invoked and the result stored if non-null.- Specified by:
computeIfAbsent
in interfaceConcurrentMap<Object,
V> - Specified by:
computeIfAbsent
in interfaceMap<Object,
V> - See Also:
-
computeIfPresent
public V computeIfPresent(Object key, BiFunction<? super Object, ? super V, ? extends V> remappingFunction) Applies the remapping function if the specified key is present and currently mapped to a non-null value. The operation is performed under a single synchronization to ensure atomicity.
- Specified by:
computeIfPresent
in interfaceConcurrentMap<Object,
V> - Specified by:
computeIfPresent
in interfaceMap<Object,
V> - See Also:
-
compute
Computes a new mapping for the specified key using the given remapping function. The entire computation occurs while synchronized on the map's write lock to provide atomic behavior.
-
remove
-
replace
-
replace
-
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 is null.
-
containsMultiKey
Returns true if this map contains a mapping for the specified N-dimensional key.- Parameters:
keys
- the key components (can be varargs)- Returns:
- true if a mapping exists for the key
-
containsKey
Map interface compatible containsKey method. Supports both single keys and N-dimensional keys via Object[] detection. Uses efficient decision tree pattern: Normal objects first, then Arrays, then Collections.- Specified by:
containsKey
in interfaceMap<Object,
V> - Parameters:
key
- either a single key or an Object[] containing multiple keys- Returns:
- true if a mapping exists for the key
-
clear
public void clear()Removes all the mappings from this map. The map will be empty after this call returns. -
values
Returns a Collection view of the values contained in this map. The collection is backed by the map's current state snapshot. -
keySet
Returns a Set view of the keys contained in this map. For MultiKeyMap, keys can be single objects or Object[] arrays. The set is backed by the map's current state snapshot. -
entrySet
Returns a Set view of the mappings contained in this map. Each entry represents a key-value mapping where the key can be a single object or an Object[] array for multi-dimensional keys. -
hashCode
public int hashCode()Returns the hash code value for this map. The hash code is computed based on all key-value pairs. -
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. -
toString
Returns a string representation of this map. Shows the key-value mappings in the format {key1=value1, key2=value2}. Handles self-references to prevent infinite recursion. -
printContentionStatistics
public void printContentionStatistics()Prints detailed contention statistics for debugging performance issues. Shows overall contention rates, stripe-level distribution, and global lock usage.
-