Class MultiKeyMap<V>

java.lang.Object
com.cedarsoftware.util.MultiKeyMap<V>
Type Parameters:
V - the type of values stored in the map
All Implemented Interfaces:
ConcurrentMap<Object,V>, Map<Object,V>

public final class MultiKeyMap<V> extends Object implements ConcurrentMap<Object,V>
High-performance N-dimensional key-value Map implementation using separate chaining.

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 (get()/put() with many keys).
  • Smart Collection Handling: Configurable behavior for Collections and Arrays — change the default automatic unpacking of Collections/Arrays capability as needed.

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.put("value1", "single-key");                       // 1D key
 mkMap.put("value2", "key1", "key2");                     // 2D key
 mkMap.put("value3", "key1", "key2", "key3");             // 3D key
 mkMap.put("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.get("key1", "key2");
 String val4 = mkMap.get("k1", "k2", "k3", "k4");
 

Collection and Array Handling:

MultiKeyMap provides flexible handling of Collections and Arrays through the MultiKeyMap.CollectionKeyMode enum:

  • MULTI_KEY_ONLY: Collections/Arrays are always unpacked into multi-key lookups
  • MULTI_KEY_FIRST: Try unpacking first, fallback to treating as single key
  • COLLECTION_KEY_FIRST: Try as single key first, fallback to unpacking

 // Configure collection handling behavior
 MultiKeyMap<String> map = new MultiKeyMap<>(1024, CollectionKeyMode.COLLECTION_KEY_FIRST);

 String[] arrayKey = {"config", "database", "url"};
 map.put(arrayKey, "jdbc:mysql://localhost:3306/db");     // Array treated as single key
 String url = map.get(arrayKey);                          // Retrieved as single 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.
  • Constructor Details

    • MultiKeyMap

      public MultiKeyMap(int capacity, float loadFactor)
      Creates a new MultiKeyMap with the specified capacity and load factor.
      Parameters:
      capacity - the initial capacity
      loadFactor - the load factor threshold for resizing
    • MultiKeyMap

      public MultiKeyMap(int capacity, float loadFactor, MultiKeyMap.CollectionKeyMode collectionKeyMode)
      Creates a new MultiKeyMap with specified capacity, load factor, and collection key behavior.
      Parameters:
      capacity - the initial capacity
      loadFactor - the load factor threshold for resizing
      collectionKeyMode - how to handle Collections/Arrays in get/remove/containsKey operations
    • MultiKeyMap

      public MultiKeyMap()
      Creates a new MultiKeyMap with default capacity (16) and default load factor (0.75).
    • MultiKeyMap

      public MultiKeyMap(MultiKeyMap.CollectionKeyMode collectionKeyMode)
      Creates a new MultiKeyMap with default capacity (16) and specified collection key behavior.
      Parameters:
      collectionKeyMode - how to handle Collections/Arrays in get/remove/containsKey operations
    • MultiKeyMap

      public MultiKeyMap(int capacity)
      Creates a new MultiKeyMap with the specified capacity and default load factor.
      Parameters:
      capacity - the initial capacity
  • Method Details

    • get

      public V get(Object... keys)
      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

      public V get(Object key)
      Map interface compatible get method with zero-allocation direct storage. Supports both single keys and N-dimensional keys via Object[] detection.
      Specified by:
      get in interface Map<Object,V>
      Parameters:
      key - either a single key or an Object[] containing multiple keys
      Returns:
      the value associated with the key, or null if not found
    • put

      public V put(V value, Object... keys)
      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 store
      keys - the key components (unlimited number)
      Returns:
      the previous value associated with the key, or null if there was no mapping
    • put

      public V put(Object key, V value)
      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
       
      Specified by:
      put in interface Map<Object,V>
      Parameters:
      key - single key, or array/Collection that will be auto-unpacked into multiple keys
      value - the value to store
      Returns:
      the previous value associated with the key, or null if there was no mapping
    • size

      public int size()
      Returns the current number of entries in the map.
      Specified by:
      size in interface Map<Object,V>
    • getMaxChainLength

      public int getMaxChainLength()
      Returns the maximum chain length encountered so far.
    • getLoadFactor

      public double getLoadFactor()
      Returns the current load factor.
    • entries

      public Iterable<MultiKeyMap.MultiKeyEntry<V>> 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.
      Specified by:
      isEmpty in interface Map<Object,V>
    • containsValue

      public boolean containsValue(Object value)
      Returns true if this map maps one or more keys to the specified value.
      Specified by:
      containsValue in interface Map<Object,V>
    • remove

      public V remove(Object... keys)
      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

      public V remove(Object key)
      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
       
      Specified by:
      remove in interface Map<Object,V>
      Parameters:
      key - single key, or array/Collection that will be auto-unpacked into multiple keys
      Returns:
      the previous value associated with the key, or null if there was no mapping
    • putAll

      public void putAll(Map<?,? extends V> m)
      Specified by:
      putAll in interface Map<Object,V>
    • putIfAbsent

      public V putIfAbsent(Object key, V value)

      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 interface ConcurrentMap<Object,V>
      Specified by:
      putIfAbsent in interface Map<Object,V>
      See Also:
    • computeIfAbsent

      public V computeIfAbsent(Object key, Function<? super Object,? extends V> mappingFunction)

      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 interface ConcurrentMap<Object,V>
      Specified by:
      computeIfAbsent in interface Map<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 interface ConcurrentMap<Object,V>
      Specified by:
      computeIfPresent in interface Map<Object,V>
      See Also:
    • compute

      public V compute(Object key, BiFunction<? super Object,? super V,? extends V> remappingFunction)

      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.

      Specified by:
      compute in interface ConcurrentMap<Object,V>
      Specified by:
      compute in interface Map<Object,V>
      See Also:
    • remove

      public boolean remove(Object key, Object value)
      Specified by:
      remove in interface ConcurrentMap<Object,V>
      Specified by:
      remove in interface Map<Object,V>
    • replace

      public V replace(Object key, V value)
      Specified by:
      replace in interface ConcurrentMap<Object,V>
      Specified by:
      replace in interface Map<Object,V>
    • replace

      public boolean replace(Object key, V oldValue, V newValue)
      Specified by:
      replace in interface ConcurrentMap<Object,V>
      Specified by:
      replace in interface Map<Object,V>
    • merge

      public V merge(Object key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

      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.

      Specified by:
      merge in interface ConcurrentMap<Object,V>
      Specified by:
      merge in interface Map<Object,V>
      See Also:
    • containsKey

      public boolean containsKey(Object... keys)
      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

      public boolean containsKey(Object key)
      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 interface Map<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.
      Specified by:
      clear in interface Map<Object,V>
    • values

      public Collection<V> values()
      Returns a Collection view of the values contained in this map. The collection is backed by the map's current state snapshot.
      Specified by:
      values in interface Map<Object,V>
    • keySet

      public Set<Object> 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.
      Specified by:
      keySet in interface Map<Object,V>
    • entrySet

      public Set<Map.Entry<Object,V>> 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.
      Specified by:
      entrySet in interface Map<Object,V>
    • hashCode

      public int hashCode()
      Returns the hash code value for this map. The hash code is computed based on all key-value pairs.
      Specified by:
      hashCode in interface Map<Object,V>
      Overrides:
      hashCode in class Object
    • equals

      public boolean equals(Object obj)
      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.
      Specified by:
      equals in interface Map<Object,V>
      Overrides:
      equals in class Object
    • toString

      public String 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.
      Overrides:
      toString in class Object
    • printContentionStatistics

      public void printContentionStatistics()
      Prints detailed contention statistics for debugging performance issues. Shows overall contention rates, stripe-level distribution, and global lock usage.