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 == NaNreturns true (unlike Java's default). This ensures consistent key lookups with floating-point values. - Zero Handling:
+0.0 == -0.0returns true in both modes (standard Java behavior). - BigDecimal Precision: Doubles are converted via
new BigDecimal(number.toString()). This means0.1dequalsBigDecimal("0.1")but NOTBigDecimal(0.1)(the latter has binary rounding errors). - Infinity Handling: Comparing
Double.POSITIVE_INFINITYorNEGATIVE_INFINITYto 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 classBuilder for creating configured MultiKeyMap instances.static enumControls 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()voidclear()Removes all the mappings from this map.Attempts to compute a mapping for the specified key and its current mapped value (ornullif 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.booleancontainsKey(Object key) Returnstrueif this map contains a mapping for the specified key.booleancontainsMultiKey(Object... keys) Returnstrueif this map contains a mapping for the specified multidimensional key using var-args syntax.booleancontainsValue(Object value) Returnstrueif this map maps one or more keys to the specified value.entries()Returns anIterableofMultiKeyMap.MultiKeyEntryobjects representing all key-value mappings in this map.entrySet()Returns aSetview of the mappings contained in this map.booleanCompares the specified object with this map for equality.Returns the value to which the specified key is mapped, ornullif this map contains no mapping for the key.booleanReturns the current case sensitivity setting for CharSequence comparisons.Returns the current collection key mode setting.booleanReturns the current dimension flattening setting.getMultiKey(Object... keys) Retrieves the value associated with the specified multidimensional key using var-args syntax.booleanReturns the current simple keys mode setting.inthashCode()Returns the hash code value for this map.booleanisEmpty()Returnstrueif this map contains no key-value mappings.keySet()Returns aSetview 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.voidPrints detailed contention statistics for this map's stripe locking system to the logger.Associates the specified value with the specified key in this map.voidCopies 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.booleanRemoves 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.booleanReplaces the entry for the specified key only if currently mapped to the specified value.intsize()Returns the number of key-value mappings in this map.toString()Returns a string representation of this map.values()Returns aCollectionview of the values contained in this map.Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, waitMethods 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:
trueif dimension flattening is enabled (all equivalent flat representations are treated as identical keys regardless of original container structure),falseif 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:
trueif simple keys mode is enabled (nested structure checks are skipped for maximum performance),falseif 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:
trueif case-sensitive comparison is enabled (default),falseif 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
nullif no mapping exists - See Also:
-
get
Returns the value to which the specified key is mapped, ornullif 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
nullif 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:
putin 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
nullif there was no mapping for the key
-
containsMultiKey
Returnstrueif 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:
trueif this map contains a mapping for the specified multi-key- See Also:
-
containsKey
Returnstrueif 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:
containsKeyin 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:
trueif 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
nullif 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:
removein 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
nullif 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()Returnstrueif 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
Returnstrueif this map maps one or more keys to the specified value.This operation requires time linear in the map size.
- Specified by:
containsValuein interfaceMap<Object,V> - Parameters:
value- the value whose presence in this map is to be tested- Returns:
trueif this map maps one or more keys to the specified value
-
keySet
Returns aSetview of the keys contained in this map.Multidimensional keys are represented as immutable List
-
values
Returns aCollectionview of the values contained in this map.Changes to the returned collection are not reflected in the map.
-
entrySet
Returns aSetview 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 keykto valuevin the specified map.- Specified by:
putAllin 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:
except that the action is performed atomically.if (!map.containsKey(key)) return map.put(key, value); else return map.get(key);- Specified by:
putIfAbsentin interfaceConcurrentMap<Object,V> - Specified by:
putIfAbsentin 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
nullif 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:
computeIfAbsentin interfaceConcurrentMap<Object,V> - Specified by:
computeIfAbsentin 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
nullif 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:
computeIfPresentin interfaceConcurrentMap<Object,V> - Specified by:
computeIfPresentin 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
nullif none - Throws:
NullPointerException- if the specified remappingFunction is null
-
compute
Attempts to compute a mapping for the specified key and its current mapped value (ornullif 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:
computein interfaceConcurrentMap<Object,V> - Specified by:
computein 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
nullif 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:
mergein interfaceConcurrentMap<Object,V> - Specified by:
mergein 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
nullif 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:
except that the action is performed atomically.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:
except that the action is performed atomically.if (map.containsKey(key)) { return map.put(key, value); } else return null;- Specified by:
replacein interfaceConcurrentMap<Object,V> - Specified by:
replacein 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
nullif 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:
except that the action is performed atomically.if (map.containsKey(key) && Objects.equals(map.get(key), oldValue)) { map.put(key, newValue); return true; } else return false;- Specified by:
replacein interfaceConcurrentMap<Object,V> - Specified by:
replacein 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:
trueif 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 mapsm1andm2, as required by the general contract ofObject.hashCode(). -
equals
Compares the specified object with this map for equality.Returns
trueif the given object is also a map and the two maps represent the same mappings. Two mapsm1andm2represent 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 anIterableofMultiKeyMap.MultiKeyEntryobjects representing all key-value mappings in this map.Each
MultiKeyEntrycontains 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
MultiKeyEntryobjects 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
-