org.apache.sis.util.collection

## Class Cache<K,V>

- Object
- - AbstractMap<K,V>
  - - Cache<K,V>

- Type Parameters:
    `K` - the type of key objects.
    `V` - the type of value objects.
  All Implemented Interfaces:
    Map<K,V>

      public class Cache<K,V>
      extends AbstractMap<K,V>

  A concurrent cache mechanism. This implementation is thread-safe and supports concurrency. A cache entry can be locked when an object is in process of being created. The steps are as below:
  1. Check if the value is already available in the map. If it is, return it immediately and we are done.
  2. Otherwise, get a lock and check again if the value is already available in the map (because the value could have been computed by an other thread between step 1 and the obtention of the lock). If it is, release the lock and we are done.
  3. Otherwise compute the value, store the result and release the lock.
  The easiest way (except for exception handling) to use this class is to prepare a `Callable` statement to be executed only if the object is not in the cache, and to invoke the `getOrCreate` method. Example:

      private final Cache<String,MyObject> cache = new Cache<String,MyObject>();

      public MyObject getMyObject(final String key) throws MyCheckedException {
          try {
              return cache.getOrCreate(key, new Callable<MyObject>() {
                  public MyObject call() throws MyCheckedException {
                      return createMyObject(key);
                  }
              });
          } catch (MyCheckedException | RuntimeException e) {
              throw e;
          } catch (Exception e) {
              throw new UndeclaredThrowableException(e);
          }
      }

  An alternative is to perform explicitly all the steps enumerated above. This alternative avoid the creation of a temporary `Callable` statement which may never be executed, and avoid the exception handling due to the `throws Exception` clause. Note that the call to `putAndUnlock` must be in the `finally` block of a `try` block beginning immediately after the call to `lock`, no matter what the result of the computation is (including `null`).

      private final Cache<String,MyObject> cache = new Cache<String,MyObject>();

      public MyObject getMyObject(final String key) throws MyCheckedException {
          MyObject value = cache.peek(key);
          if (value == null) {
              final Cache.Handler<MyObject> handler = cache.lock(key);
              try {
                  value = handler.peek();
                  if (value == null) {
                      value = createMyObject(key);
                  }
              } finally {
                  handler.putAndUnlock(value);
              }
          }
          return value;
      }

  Eviction of eldest values
  - The cost of a value is the value returned by `cost(V)`. The default implementation returns 1 in all cases, but subclasses can override this method for more elaborated cost computation.
  - The total cost is the sum of the cost of all values held by strong reference in this cache. The total cost does not include the cost of values held by weak or soft reference.
  - The cost limit is the maximal value allowed for the total cost. If the total cost exceed this value, then strong references to the eldest values are replaced by weak or soft references until the total cost become equals or lower than the cost limit.
  The total cost is given at construction time. If the `cost(V)` method has not been overridden, then the total cost is the maximal amount of values to keep by strong references.
  Circular dependencies
  This implementation assumes that there is no circular dependencies (or cyclic graph) between the values in the cache. For example if creating A implies creating B, then creating B is not allowed to implies (directly or indirectly) the creation of A. If this rule is not meet, deadlock may occur randomly.
  Since:
    0.3

  Defined in the `sis-utility` module

- - ### Nested Class Summary

    Nested Classes
    Modifier and Type | Class and Description
    --- | ---
    `static interface` | `Cache.Handler<V>` The handler returned by `lock(K)`, to be used for unlocking and storing the result.

    - ### Nested classes/interfaces inherited from class AbstractMap

      `AbstractMap​.SimpleEntry<K,V>, AbstractMap​.SimpleImmutableEntry<K,V>`

    - ### Nested classes/interfaces inherited from interface Map

      `Map​.Entry<K,V>`

  - ### Constructor Summary

    Constructors
    Constructor and Description
    `Cache()` Creates a new cache with a default initial capacity and cost limit of 100.
    `Cache(int initialCapacity, long costLimit, boolean soft)` Creates a new cache using the given initial capacity and cost limit.

  - ### Method Summary

    All Methods Instance Methods Concrete Methods
    Modifier and Type | Method and Description
    --- | ---
    `void` | `clear()` Clears the content of this cache.
    `boolean` | `containsKey(Object key)` Returns `true` if this map contains the specified key.
    `protected int` | `cost(V value)` Computes an estimation of the cost of the given value.
    `Set<Map.Entry<K,V>>` | `entrySet()` Returns the set of entries in this cache.
    `V` | `get(Object key)` Returns the value associated to the given key in the cache.
    `V` | `getOrCreate(K key, Callable<? extends V> creator)` Returns the value for the given key.
    `boolean` | `isEmpty()` Returns `true` if this cache is empty.
    `boolean` | `isKeyCollisionAllowed()` Returns `true` if different values may be assigned to the same key.
    `Set<K>` | `keySet()` Returns the set of keys in this cache.
    `Cache.Handler<V>` | `lock(K key)` Gets a lock for the entry at the given key and returns a handler to be used by the caller for unlocking and storing the result.
    `V` | `peek(K key)` If a value is already cached for the given key, returns it.
    `V` | `put(K key, V value)` Puts the given value in cache.
    `V` | `remove(Object key)` Removes the value associated to the given key in the cache.
    `void` | `setKeyCollisionAllowed(boolean allowed)` If set to `true`, different values may be assigned to the same key.
    `int` | `size()` Returns the number of elements in this cache.

    - ### Methods inherited from class AbstractMap

      `clone, containsValue, equals, hashCode, putAll, toString, values`

    - ### Methods inherited from class Object

      `finalize, getClass, notify, notifyAll, wait, wait, wait`

    - ### Methods inherited from interface Map

      `compute, computeIfAbsent, computeIfPresent, forEach, getOrDefault, merge, putIfAbsent, remove, replace, replace, replaceAll`

- - ### Constructor Detail

    - #### Cache

          public Cache()

      Creates a new cache with a default initial capacity and cost limit of 100. The oldest objects will be hold by weak references.

    - #### Cache

          public Cache(int initialCapacity,
                       long costLimit,
                       boolean soft)

      Creates a new cache using the given initial capacity and cost limit. The initial capacity is the expected number of values to be stored in this cache. More values are allowed, but a little bit of CPU time may be saved if the expected capacity is known before the cache is created.

      The cost limit is the maximal value of the total cost (the sum of the cost of all values) before to replace eldest strong references by weak or soft references.

      Parameters:
        `initialCapacity` - the initial capacity.
        `costLimit` - the maximum number of objects to keep by strong reference.
        `soft` - if `true`, use `SoftReference` instead of `WeakReference`.

  - ### Method Detail

    - #### clear

          public void clear()

      Clears the content of this cache.
      Specified by:
        `clear` in interface `Map<K,V>`
      Overrides:
        `clear` in class `AbstractMap<K,V>`

    - #### isEmpty

          public boolean isEmpty()

      Returns `true` if this cache is empty.
      Specified by:
        `isEmpty` in interface `Map<K,V>`
      Overrides:
        `isEmpty` in class `AbstractMap<K,V>`
      Returns:
        `true` if this cache do not contains any element.

    - #### size

          public int size()

      Returns the number of elements in this cache. The count includes values keep by strong, soft or weak references, and the values under computation at the time this method is invoked.
      Specified by:
        `size` in interface `Map<K,V>`
      Overrides:
        `size` in class `AbstractMap<K,V>`
      Returns:
        the number of elements currently cached.

    - #### containsKey

          public boolean containsKey(Object key)

      Returns `true` if this map contains the specified key.
      Specified by:
        `containsKey` in interface `Map<K,V>`
      Overrides:
        `containsKey` in class `AbstractMap<K,V>`
      Parameters:
        `key` - the key to check for existence.
      Returns:
        `true` if the given key still exist in this cache.

    - #### put

          public V put(K key,
                       V value)

      Puts the given value in cache.
      Specified by:
        `put` in interface `Map<K,V>`
      Overrides:
        `put` in class `AbstractMap<K,V>`
      Parameters:
        `key` - the key for which to set a value.
        `value` - the value to store.
      Returns:
        the value previously stored at the given key, or `null` if none.

    - #### remove

          public V remove(Object key)

      Removes the value associated to the given key in the cache.
      Specified by:
        `remove` in interface `Map<K,V>`
      Overrides:
        `remove` in class `AbstractMap<K,V>`
      Parameters:
        `key` - the key of the value to removed.
      Returns:
        the value that were associated to the given key, or `null` if none.

    - #### get

          public V get(Object key)

      Returns the value associated to the given key in the cache. This method is similar to `peek(K)` except that it blocks if the value is currently under computation in an other thread.
      Specified by:
        `get` in interface `Map<K,V>`
      Overrides:
        `get` in class `AbstractMap<K,V>`
      Parameters:
        `key` - the key of the value to get.
      Returns:
        the value associated to the given key, or `null` if none.

    - #### getOrCreate

          public V getOrCreate(K key,
                               Callable<? extends V> creator)
                        throws Exception

      Returns the value for the given key. If a value already exists in the cache, then it is returned immediately. Otherwise the `creator​.call()` method is invoked and its result is saved in this cache for future reuse.
      Parameters:
        `key` - the key for which to get the cached or created value.
        `creator` - a method for creating a value, to be invoked only if no value are cached for the given key.
      Returns:
        the value for the given key, which may have been created as a result of this method call.
      Throws:
        `Exception` - if an exception occurred during the execution of `creator​.call()`.

    - #### peek

          public V peek(K key)

      If a value is already cached for the given key, returns it. Otherwise returns `null`. This method is similar to `get(Object)` except that it doesn't block if the value is in process of being computed in an other thread; it returns `null` in such case.
      Parameters:
        `key` - the key for which to get the cached value.
      Returns:
        the cached value for the given key, or `null` if there is none.

    - #### lock

          public Cache.Handler<V> lock(K key)

      Gets a lock for the entry at the given key and returns a handler to be used by the caller for unlocking and storing the result. This method must be used together with a `putAndUnlock` call in `try` … `catch` blocks as in the example below:

          Cache.Handler handler = cache.lock();
          try {
              // Compute the result...
          } finally {
              handler.putAndUnlock(result);
          }

      Parameters:
        `key` - the key for the entry to lock.
      Returns:
        a handler to use for unlocking and storing the result.

    - #### keySet

          public Set<K> keySet()

      Returns the set of keys in this cache. The returned set is subjects to the same caution than the ones documented in the `ConcurrentHashMap​.keySet()` method.
      Specified by:
        `keySet` in interface `Map<K,V>`
      Overrides:
        `keySet` in class `AbstractMap<K,V>`
      Returns:
        the set of keys in this cache.

    - #### entrySet

          public Set<Map.Entry<K,V>> entrySet()

      Returns the set of entries in this cache. The returned set is subjects to the same caution than the ones documented in the `ConcurrentHashMap​.entrySet()` method, except that it doesn't support removal of elements (including through the `Iterator​.remove()` method call).
      Specified by:
        `entrySet` in interface `Map<K,V>`
      Specified by:
        `entrySet` in class `AbstractMap<K,V>`
      Returns:
        a view of the entries contained in this map.

    - #### isKeyCollisionAllowed

          public boolean isKeyCollisionAllowed()

      Returns `true` if different values may be assigned to the same key. The default value is `false`.
      Returns:
        `true` if key collisions are allowed.

    - #### setKeyCollisionAllowed

          public void setKeyCollisionAllowed(boolean allowed)

      If set to `true`, different values may be assigned to the same key. This is usually an error, so the default `Cache` behavior is to thrown an `IllegalStateException` in such cases, typically when `Cache​.Handler​.putAndUnlock(Object)` is invoked. However in some cases we may want to relax this check. For example the EPSG database sometime assigns the same key to different kind of objects.

      If key collisions are allowed and two threads invoke `lock(Object)` concurrently for the same key, then the value to be stored in the map will be the one computed by the first thread who got the lock. The value computed by any other concurrent thread will be ignored by this `Cache` class. However those threads still return their computed values to their callers.

      This property can also be set in order to allow some recursivity. If during the creation of an object, the program asks to this `Cache` for the same object (using the same key), then the default `Cache` implementation will consider this situation as a key collision unless this property has been set to `true`.

      Parameters:
        `allowed` - `true` if key collisions should be allowed.

    - #### cost

          protected int cost(V value)

      Computes an estimation of the cost of the given value. The default implementation returns 1 in all cases. Subclasses should override this method if they have some easy way to measure the relative cost of value objects.
      Parameters:
        `value` - the object for which to get an estimation of its cost.
      Returns:
        the estimated cost of the given object.
      See Also:
        `Instrumentation​.getObjectSize(Object)`

Copyright © 2010–2017 The Apache Software Foundation. All rights reserved.