Class MapMaker


  • @GwtCompatible(emulated=true)
    public final class MapMaker
    extends java.lang.Object

    A builder of ConcurrentMap instances having any combination of the following features:

    • keys or values automatically wrapped in weak or soft references
    • notification of evicted (or otherwise removed) entries

    Usage example:

       
    
       ConcurrentMap<Request, Stopwatch> timers = new MapMaker()
           .concurrencyLevel(4)
           .weakKeys()
           .makeMap();

    These features are all optional; new MapMaker().makeMap() returns a valid concurrent map that behaves similarly to a ConcurrentHashMap.

    The returned map is implemented as a hash table with similar performance characteristics to ConcurrentHashMap. It supports all optional operations of the ConcurrentMap interface. It does not permit null keys or values.

    Note: by default, the returned map uses equality comparisons (the equals method) to determine equality for keys or values. However, if weakKeys() was specified, the map uses identity (==) comparisons instead for keys. Likewise, if weakValues() or softValues() was specified, the map uses identity comparisons for values.

    The view collections of the returned map have weakly consistent iterators. This means that they are safe for concurrent use, but if other threads modify the map after the iterator is created, it is undefined which of these changes, if any, are reflected in that iterator. These iterators never throw ConcurrentModificationException.

    If weakKeys(), weakValues(), or softValues() are requested, it is possible for a key or value present in the map to be reclaimed by the garbage collector. Entries with reclaimed keys or values may be removed from the map on each map modification or on occasional map accesses; such entries may be counted by Map.size(), but will never be visible to read or write operations. A partially-reclaimed entry is never exposed to the user. Any Map.Entry instance retrieved from the map's entry set is a snapshot of that entry's state at the time of retrieval; such entries do, however, support Map.Entry.setValue(V), which simply calls Map.put(K, V) on the entry's key.

    The maps produced by MapMaker are serializable, and the deserialized maps retain all the configuration properties of the original map. During deserialization, if the original map had used soft or weak references, the entries are reconstructed as they were, but it's not unlikely they'll be quickly garbage-collected before they are ever accessed.

    new MapMaker().weakKeys().makeMap() is a recommended replacement for WeakHashMap, but note that it compares keys using object identity whereas WeakHashMap uses Object.equals(java.lang.Object).

    Since:
    2.0
    Author:
    Bob Lee, Charles Fry, Kevin Bourrillion
    • Constructor Detail

      • MapMaker

        public MapMaker()
        Constructs a new MapMaker instance with default settings, including strong keys, strong values, and no automatic eviction of any kind.
    • Method Detail

      • initialCapacity

        public MapMaker initialCapacity​(int initialCapacity)
        Sets the minimum total size for the internal hash tables. For example, if the initial capacity is 60, and the concurrency level is 8, then eight segments are created, each having a hash table of size eight. Providing a large enough estimate at construction time avoids the need for expensive resizing operations later, but setting this value unnecessarily high wastes memory.
        Throws:
        java.lang.IllegalArgumentException - if initialCapacity is negative
        java.lang.IllegalStateException - if an initial capacity was already set
      • concurrencyLevel

        public MapMaker concurrencyLevel​(int concurrencyLevel)
        Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The table is internally partitioned to try to permit the indicated number of concurrent updates without contention. Because assignment of entries to these partitions is not necessarily uniform, the actual concurrency observed may vary. Ideally, you should choose a value to accommodate as many threads as will ever concurrently modify the table. Using a significantly higher value than you need can waste space and time, and a significantly lower value can lead to thread contention. But overestimates and underestimates within an order of magnitude do not usually have much noticeable impact. A value of one permits only one thread to modify the map at a time, but since read operations can proceed concurrently, this still yields higher concurrency than full synchronization. Defaults to 4.

        Note: Prior to Guava release 9.0, the default was 16. It is possible the default will change again in the future. If you care about this value, you should always choose it explicitly.

        Throws:
        java.lang.IllegalArgumentException - if concurrencyLevel is nonpositive
        java.lang.IllegalStateException - if a concurrency level was already set
      • weakKeys

        @GwtIncompatible("java.lang.ref.WeakReference")
        public MapMaker weakKeys()
        Specifies that each key (not value) stored in the map should be wrapped in a WeakReference (by default, strong references are used).

        Warning: when this method is used, the resulting map will use identity (==) comparison to determine equality of keys, which is a technical violation of the Map specification, and may not be what you expect.

        Throws:
        java.lang.IllegalStateException - if the key strength was already set
        See Also:
        WeakReference
      • weakValues

        @GwtIncompatible("java.lang.ref.WeakReference")
        public MapMaker weakValues()
        Specifies that each value (not key) stored in the map should be wrapped in a WeakReference (by default, strong references are used).

        Weak values will be garbage collected once they are weakly reachable. This makes them a poor candidate for caching; consider softValues() instead.

        Warning: when this method is used, the resulting map will use identity (==) comparison to determine equality of values. This technically violates the specifications of the methods containsValue, remove(Object, Object) and replace(K, V, V), and may not be what you expect.

        Throws:
        java.lang.IllegalStateException - if the value strength was already set
        See Also:
        WeakReference
      • makeMap

        public <K,​V> java.util.concurrent.ConcurrentMap<K,​V> makeMap()
        Builds a thread-safe map. This method does not alter the state of this MapMaker instance, so it can be invoked again to create multiple independent maps.

        The bulk operations putAll, equals, and clear are not guaranteed to be performed atomically on the returned map. Additionally, size and containsValue are implemented as bulk read operations, and thus may fail to observe concurrent writes.

        Returns:
        a serializable concurrent map having the requested features
      • makeComputingMap

        @Deprecated
        public <K,​V> java.util.concurrent.ConcurrentMap<K,​V> makeComputingMap​(Function<? super K,​? extends V> computingFunction)
        Builds a map that supports atomic, on-demand computation of values. Map.get(java.lang.Object) either returns an already-computed value for the given key, atomically computes it using the supplied function, or, if another thread is currently computing the value for this key, simply waits for that thread to finish and returns its computed value. Note that the function may be executed concurrently by multiple threads, but only for distinct keys.

        New code should use CacheBuilder, which supports statistics collection, introduces the CacheLoader interface for loading entries into the cache (allowing checked exceptions to be thrown in the process), and more cleanly separates computation from the cache's Map view.

        If an entry's value has not finished computing yet, query methods besides get return immediately as if an entry doesn't exist. In other words, an entry isn't externally visible until the value's computation completes.

        Map.get(java.lang.Object) on the returned map will never return null. It may throw:

        • NullPointerException if the key is null or the computing function returns a null result
        • ComputationException if an exception was thrown by the computing function. If that exception is already of type ComputationException it is propagated directly; otherwise it is wrapped.

        Note: Callers of get must ensure that the key argument is of type K. The get method accepts Object, so the key type is not checked at compile time. Passing an object of a type other than K can result in that object being unsafely passed to the computing function as type K, and unsafely stored in the map.

        If Map.put(K, V) is called before a computation completes, other threads waiting on the computation will wake up and return the stored value.

        This method does not alter the state of this MapMaker instance, so it can be invoked again to create multiple independent maps.

        Insertion, removal, update, and access operations on the returned map safely execute concurrently by multiple threads. Iterators on the returned map are weakly consistent, returning elements reflecting the state of the map at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException, and may proceed concurrently with other operations.

        The bulk operations putAll, equals, and clear are not guaranteed to be performed atomically on the returned map. Additionally, size and containsValue are implemented as bulk read operations, and thus may fail to observe concurrent writes.

        Parameters:
        computingFunction - the function used to compute new values
        Returns:
        a serializable concurrent map having the requested features
      • toString

        public java.lang.String toString()
        Returns a string representation for this MapMaker instance. The exact form of the returned string is not specificed.
        Overrides:
        toString in class java.lang.Object