Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.google.common.collect;
     16 
     17 import static com.google.common.base.Objects.firstNonNull;
     18 import static com.google.common.base.Preconditions.checkArgument;
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 import static com.google.common.base.Preconditions.checkState;
     21 
     22 import com.google.common.annotations.GwtCompatible;
     23 import com.google.common.annotations.GwtIncompatible;
     24 import com.google.common.base.Ascii;
     25 import com.google.common.base.Equivalence;
     26 import com.google.common.base.Equivalences;
     27 import com.google.common.base.Function;
     28 import com.google.common.base.Objects;
     29 import com.google.common.base.Throwables;
     30 import com.google.common.base.Ticker;
     31 import com.google.common.collect.MapMakerInternalMap.Strength;
     32 
     33 import java.io.Serializable;
     34 import java.lang.ref.SoftReference;
     35 import java.lang.ref.WeakReference;
     36 import java.util.AbstractMap;
     37 import java.util.Collections;
     38 import java.util.ConcurrentModificationException;
     39 import java.util.Map;
     40 import java.util.Set;
     41 import java.util.concurrent.ConcurrentHashMap;
     42 import java.util.concurrent.ConcurrentMap;
     43 import java.util.concurrent.ExecutionException;
     44 import java.util.concurrent.TimeUnit;
     45 
     46 import javax.annotation.Nullable;
     47 
     48 /**
     49  * <p>A builder of {@link ConcurrentMap} instances having any combination of the following features:
     50  *
     51  * <ul>
     52  * <li>keys or values automatically wrapped in {@linkplain WeakReference weak} or {@linkplain
     53  *     SoftReference soft} references
     54  * <li>least-recently-used eviction when a maximum size is exceeded
     55  * <li>time-based expiration of entries, measured since last access or last write
     56  * <li>notification of evicted (or otherwise removed) entries
     57  * <li>on-demand computation of values for keys not already present
     58  * </ul>
     59  *
     60  * <p>Usage example: <pre>   {@code
     61  *
     62  *   ConcurrentMap<Key, Graph> graphs = new MapMaker()
     63  *       .concurrencyLevel(4)
     64  *       .weakKeys()
     65  *       .maximumSize(10000)
     66  *       .expireAfterWrite(10, TimeUnit.MINUTES)
     67  *       .makeComputingMap(
     68  *           new Function<Key, Graph>() {
     69  *             public Graph apply(Key key) {
     70  *               return createExpensiveGraph(key);
     71  *             }
     72  *           });}</pre>
     73  *
     74  * These features are all optional; {@code new MapMaker().makeMap()} returns a valid concurrent map
     75  * that behaves similarly to a {@link ConcurrentHashMap}.
     76  *
     77  * <p>The returned map is implemented as a hash table with similar performance characteristics to
     78  * {@link ConcurrentHashMap}. It supports all optional operations of the {@code ConcurrentMap}
     79  * interface. It does not permit null keys or values.
     80  *
     81  * <p><b>Note:</b> by default, the returned map uses equality comparisons (the {@link Object#equals
     82  * equals} method) to determine equality for keys or values. However, if {@link #weakKeys} or {@link
     83  * #softKeys} was specified, the map uses identity ({@code ==}) comparisons instead for keys.
     84  * Likewise, if {@link #weakValues} or {@link #softValues} was specified, the map uses identity
     85  * comparisons for values.
     86  *
     87  * <p>The view collections of the returned map have <i>weakly consistent iterators</i>. This means
     88  * that they are safe for concurrent use, but if other threads modify the map after the iterator is
     89  * created, it is undefined which of these changes, if any, are reflected in that iterator. These
     90  * iterators never throw {@link ConcurrentModificationException}.
     91  *
     92  * <p>If soft or weak references were requested, it is possible for a key or value present in the
     93  * the map to be reclaimed by the garbage collector. If this happens, the entry automatically
     94  * disappears from the map. A partially-reclaimed entry is never exposed to the user. Any {@link
     95  * java.util.Map.Entry} instance retrieved from the map's {@linkplain Map#entrySet entry set} is a
     96  * snapshot of that entry's state at the time of retrieval; such entries do, however, support {@link
     97  * java.util.Map.Entry#setValue}, which simply calls {@link Map#put} on the entry's key.
     98  *
     99  * <p>The maps produced by {@code MapMaker} are serializable, and the deserialized maps retain all
    100  * the configuration properties of the original map. During deserialization, if the original map had
    101  * used soft or weak references, the entries are reconstructed as they were, but it's not unlikely
    102  * they'll be quickly garbage-collected before they are ever accessed.
    103  *
    104  * <p>{@code new MapMaker().weakKeys().makeMap()} is a recommended replacement for {@link
    105  * java.util.WeakHashMap}, but note that it compares keys using object identity whereas {@code
    106  * WeakHashMap} uses {@link Object#equals}.
    107  *
    108  * @author Bob Lee
    109  * @author Charles Fry
    110  * @author Kevin Bourrillion
    111  * @since 2.0 (imported from Google Collections Library)
    112  */
    113 @GwtCompatible(emulated = true)
    114 public final class MapMaker extends GenericMapMaker<Object, Object> {
    115   private static final int DEFAULT_INITIAL_CAPACITY = 16;
    116   private static final int DEFAULT_CONCURRENCY_LEVEL = 4;
    117   private static final int DEFAULT_EXPIRATION_NANOS = 0;
    118 
    119   static final int UNSET_INT = -1;
    120 
    121   // TODO(kevinb): dispense with this after benchmarking
    122   boolean useCustomMap;
    123 
    124   int initialCapacity = UNSET_INT;
    125   int concurrencyLevel = UNSET_INT;
    126   int maximumSize = UNSET_INT;
    127 
    128   Strength keyStrength;
    129   Strength valueStrength;
    130 
    131   long expireAfterWriteNanos = UNSET_INT;
    132   long expireAfterAccessNanos = UNSET_INT;
    133 
    134   RemovalCause nullRemovalCause;
    135 
    136   Equivalence<Object> keyEquivalence;
    137   Equivalence<Object> valueEquivalence;
    138 
    139   Ticker ticker;
    140 
    141   /**
    142    * Constructs a new {@code MapMaker} instance with default settings, including strong keys, strong
    143    * values, and no automatic eviction of any kind.
    144    */
    145   public MapMaker() {}
    146 
    147   private boolean useNullMap() {
    148     return (nullRemovalCause == null);
    149   }
    150 
    151   /**
    152    * Sets a custom {@code Equivalence} strategy for comparing keys.
    153    *
    154    * <p>By default, the map uses {@link Equivalences#identity} to determine key equality when
    155    * {@link #weakKeys} or {@link #softKeys} is specified, and {@link Equivalences#equals()}
    156    * otherwise.
    157    */
    158   @GwtIncompatible("To be supported")
    159   @Override
    160   MapMaker keyEquivalence(Equivalence<Object> equivalence) {
    161     checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence);
    162     keyEquivalence = checkNotNull(equivalence);
    163     this.useCustomMap = true;
    164     return this;
    165   }
    166 
    167   Equivalence<Object> getKeyEquivalence() {
    168     return firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence());
    169   }
    170 
    171   /**
    172    * Sets a custom {@code Equivalence} strategy for comparing values.
    173    *
    174    * <p>By default, the map uses {@link Equivalences#identity} to determine value equality when
    175    * {@link #weakValues} or {@link #softValues} is specified, and {@link Equivalences#equals()}
    176    * otherwise.
    177    */
    178   @GwtIncompatible("To be supported")
    179   @Override
    180   MapMaker valueEquivalence(Equivalence<Object> equivalence) {
    181     checkState(valueEquivalence == null,
    182         "value equivalence was already set to %s", valueEquivalence);
    183     this.valueEquivalence = checkNotNull(equivalence);
    184     this.useCustomMap = true;
    185     return this;
    186   }
    187 
    188   Equivalence<Object> getValueEquivalence() {
    189     return firstNonNull(valueEquivalence, getValueStrength().defaultEquivalence());
    190   }
    191 
    192   /**
    193    * Sets the minimum total size for the internal hash tables. For example, if the initial capacity
    194    * is {@code 60}, and the concurrency level is {@code 8}, then eight segments are created, each
    195    * having a hash table of size eight. Providing a large enough estimate at construction time
    196    * avoids the need for expensive resizing operations later, but setting this value unnecessarily
    197    * high wastes memory.
    198    *
    199    * @throws IllegalArgumentException if {@code initialCapacity} is negative
    200    * @throws IllegalStateException if an initial capacity was already set
    201    */
    202   @Override
    203   public MapMaker initialCapacity(int initialCapacity) {
    204     checkState(this.initialCapacity == UNSET_INT, "initial capacity was already set to %s",
    205         this.initialCapacity);
    206     checkArgument(initialCapacity >= 0);
    207     this.initialCapacity = initialCapacity;
    208     return this;
    209   }
    210 
    211   int getInitialCapacity() {
    212     return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity;
    213   }
    214 
    215   /**
    216    * Specifies the maximum number of entries the map may contain. Note that the map <b>may evict an
    217    * entry before this limit is exceeded</b>. As the map size grows close to the maximum, the map
    218    * evicts entries that are less likely to be used again. For example, the map may evict an entry
    219    * because it hasn't been used recently or very often.
    220    *
    221    * <p>When {@code size} is zero, elements can be successfully added to the map, but are evicted
    222    * immediately. This has the same effect as invoking {@link #expireAfterWrite
    223    * expireAfterWrite}{@code (0, unit)} or {@link #expireAfterAccess expireAfterAccess}{@code (0,
    224    * unit)}. It can be useful in testing, or to disable caching temporarily without a code change.
    225    *
    226    * <p>Caching functionality in {@code MapMaker} is being moved to
    227    * {@link com.google.common.cache.CacheBuilder}.
    228    *
    229    * @param size the maximum size of the map
    230    * @throws IllegalArgumentException if {@code size} is negative
    231    * @throws IllegalStateException if a maximum size was already set
    232    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    233    *     {@link com.google.common.cache.CacheBuilder}, with {@link #maximumSize} being
    234    *     replaced by {@link com.google.common.cache.CacheBuilder#maximumSize}.
    235    */
    236   @Deprecated
    237   @Override
    238   MapMaker maximumSize(int size) {
    239     checkState(this.maximumSize == UNSET_INT, "maximum size was already set to %s",
    240         this.maximumSize);
    241     checkArgument(size >= 0, "maximum size must not be negative");
    242     this.maximumSize = size;
    243     this.useCustomMap = true;
    244     if (maximumSize == 0) {
    245       // SIZE trumps EXPIRED
    246       this.nullRemovalCause = RemovalCause.SIZE;
    247     }
    248     return this;
    249   }
    250 
    251   /**
    252    * Guides the allowed concurrency among update operations. Used as a hint for internal sizing. The
    253    * table is internally partitioned to try to permit the indicated number of concurrent updates
    254    * without contention. Because assignment of entries to these partitions is not necessarily
    255    * uniform, the actual concurrency observed may vary. Ideally, you should choose a value to
    256    * accommodate as many threads as will ever concurrently modify the table. Using a significantly
    257    * higher value than you need can waste space and time, and a significantly lower value can lead
    258    * to thread contention. But overestimates and underestimates within an order of magnitude do not
    259    * usually have much noticeable impact. A value of one permits only one thread to modify the map
    260    * at a time, but since read operations can proceed concurrently, this still yields higher
    261    * concurrency than full synchronization. Defaults to 4.
    262    *
    263    * <p><b>Note:</b> Prior to Guava release 9.0, the default was 16. It is possible the default will
    264    * change again in the future. If you care about this value, you should always choose it
    265    * explicitly.
    266    *
    267    * @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
    268    * @throws IllegalStateException if a concurrency level was already set
    269    */
    270   @Override
    271   public MapMaker concurrencyLevel(int concurrencyLevel) {
    272     checkState(this.concurrencyLevel == UNSET_INT, "concurrency level was already set to %s",
    273         this.concurrencyLevel);
    274     checkArgument(concurrencyLevel > 0);
    275     this.concurrencyLevel = concurrencyLevel;
    276     return this;
    277   }
    278 
    279   int getConcurrencyLevel() {
    280     return (concurrencyLevel == UNSET_INT) ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel;
    281   }
    282 
    283   /**
    284    * Specifies that each key (not value) stored in the map should be strongly referenced.
    285    *
    286    * @throws IllegalStateException if the key strength was already set
    287    */
    288   @Override
    289   MapMaker strongKeys() {
    290     return setKeyStrength(Strength.STRONG);
    291   }
    292 
    293   /**
    294    * Specifies that each key (not value) stored in the map should be wrapped in a {@link
    295    * WeakReference} (by default, strong references are used).
    296    *
    297    * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==})
    298    * comparison to determine equality of keys, which is a technical violation of the {@link Map}
    299    * specification, and may not be what you expect.
    300    *
    301    * @throws IllegalStateException if the key strength was already set
    302    * @see WeakReference
    303    */
    304   @GwtIncompatible("java.lang.ref.WeakReference")
    305   @Override
    306   public MapMaker weakKeys() {
    307     return setKeyStrength(Strength.WEAK);
    308   }
    309 
    310   /**
    311    * <b>This method is broken.</b> Maps with soft keys offer no functional advantage over maps with
    312    * weak keys, and they waste memory by keeping unreachable elements in the map. If your goal is to
    313    * create a memory-sensitive map, then consider using soft values instead.
    314    *
    315    * <p>Specifies that each key (not value) stored in the map should be wrapped in a
    316    * {@link SoftReference} (by default, strong references are used). Softly-referenced objects will
    317    * be garbage-collected in a <i>globally</i> least-recently-used manner, in response to memory
    318    * demand.
    319    *
    320    * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==})
    321    * comparison to determine equality of keys, which is a technical violation of the {@link Map}
    322    * specification, and may not be what you expect.
    323    *
    324    * @throws IllegalStateException if the key strength was already set
    325    * @see SoftReference
    326    * @deprecated use {@link #softValues} to create a memory-sensitive map, or {@link #weakKeys} to
    327    *     create a map that doesn't hold strong references to the keys.
    328    *     <b>This method is scheduled for deletion in January 2013.</b>
    329    */
    330   @Deprecated
    331   @GwtIncompatible("java.lang.ref.SoftReference")
    332   @Override
    333   public MapMaker softKeys() {
    334     return setKeyStrength(Strength.SOFT);
    335   }
    336 
    337   MapMaker setKeyStrength(Strength strength) {
    338     checkState(keyStrength == null, "Key strength was already set to %s", keyStrength);
    339     keyStrength = checkNotNull(strength);
    340     if (strength != Strength.STRONG) {
    341       // STRONG could be used during deserialization.
    342       useCustomMap = true;
    343     }
    344     return this;
    345   }
    346 
    347   Strength getKeyStrength() {
    348     return firstNonNull(keyStrength, Strength.STRONG);
    349   }
    350 
    351   /**
    352    * Specifies that each value (not key) stored in the map should be strongly referenced.
    353    *
    354    * @throws IllegalStateException if the value strength was already set
    355    */
    356   @Override
    357   MapMaker strongValues() {
    358     return setValueStrength(Strength.STRONG);
    359   }
    360 
    361   /**
    362    * Specifies that each value (not key) stored in the map should be wrapped in a
    363    * {@link WeakReference} (by default, strong references are used).
    364    *
    365    * <p>Weak values will be garbage collected once they are weakly reachable. This makes them a poor
    366    * candidate for caching; consider {@link #softValues} instead.
    367    *
    368    * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==})
    369    * comparison to determine equality of values. This technically violates the specifications of
    370    * the methods {@link Map#containsValue containsValue},
    371    * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)} and
    372    * {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}, and may not be what you
    373    * expect.
    374    *
    375    * @throws IllegalStateException if the value strength was already set
    376    * @see WeakReference
    377    */
    378   @GwtIncompatible("java.lang.ref.WeakReference")
    379   @Override
    380   public MapMaker weakValues() {
    381     return setValueStrength(Strength.WEAK);
    382   }
    383 
    384   /**
    385    * Specifies that each value (not key) stored in the map should be wrapped in a
    386    * {@link SoftReference} (by default, strong references are used). Softly-referenced objects will
    387    * be garbage-collected in a <i>globally</i> least-recently-used manner, in response to memory
    388    * demand.
    389    *
    390    * <p><b>Warning:</b> in most circumstances it is better to set a per-cache {@linkplain
    391    * #maximumSize maximum size} instead of using soft references. You should only use this method if
    392    * you are well familiar with the practical consequences of soft references.
    393    *
    394    * <p><b>Warning:</b> when this method is used, the resulting map will use identity ({@code ==})
    395    * comparison to determine equality of values. This technically violates the specifications of
    396    * the methods {@link Map#containsValue containsValue},
    397    * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)} and
    398    * {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}, and may not be what you
    399    * expect.
    400    *
    401    * @throws IllegalStateException if the value strength was already set
    402    * @see SoftReference
    403    */
    404   @GwtIncompatible("java.lang.ref.SoftReference")
    405   @Override
    406   public MapMaker softValues() {
    407     return setValueStrength(Strength.SOFT);
    408   }
    409 
    410   MapMaker setValueStrength(Strength strength) {
    411     checkState(valueStrength == null, "Value strength was already set to %s", valueStrength);
    412     valueStrength = checkNotNull(strength);
    413     if (strength != Strength.STRONG) {
    414       // STRONG could be used during deserialization.
    415       useCustomMap = true;
    416     }
    417     return this;
    418   }
    419 
    420   Strength getValueStrength() {
    421     return firstNonNull(valueStrength, Strength.STRONG);
    422   }
    423 
    424   /**
    425    * Old name of {@link #expireAfterWrite}.
    426    *
    427    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    428    *     {@link com.google.common.cache.CacheBuilder}. Functionality equivalent to
    429    *     {@link MapMaker#expiration} is provided by
    430    *     {@link com.google.common.cache.CacheBuilder#expireAfterWrite}.
    431    *     <b>This method is scheduled for deletion in July 2012.</b>
    432    */
    433   @Deprecated
    434   @Override
    435   public
    436   MapMaker expiration(long duration, TimeUnit unit) {
    437     return expireAfterWrite(duration, unit);
    438   }
    439 
    440   /**
    441    * Specifies that each entry should be automatically removed from the map once a fixed duration
    442    * has elapsed after the entry's creation, or the most recent replacement of its value.
    443    *
    444    * <p>When {@code duration} is zero, elements can be successfully added to the map, but are
    445    * evicted immediately. This has a very similar effect to invoking {@link #maximumSize
    446    * maximumSize}{@code (0)}. It can be useful in testing, or to disable caching temporarily without
    447    * a code change.
    448    *
    449    * <p>Expired entries may be counted by {@link Map#size}, but will never be visible to read or
    450    * write operations. Expired entries are currently cleaned up during write operations, or during
    451    * occasional read operations in the absense of writes; though this behavior may change in the
    452    * future.
    453    *
    454    * @param duration the length of time after an entry is created that it should be automatically
    455    *     removed
    456    * @param unit the unit that {@code duration} is expressed in
    457    * @throws IllegalArgumentException if {@code duration} is negative
    458    * @throws IllegalStateException if the time to live or time to idle was already set
    459    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    460    *     {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterWrite} being
    461    *     replaced by {@link com.google.common.cache.CacheBuilder#expireAfterWrite}.
    462    */
    463   @Deprecated
    464   @Override
    465   MapMaker expireAfterWrite(long duration, TimeUnit unit) {
    466     checkExpiration(duration, unit);
    467     this.expireAfterWriteNanos = unit.toNanos(duration);
    468     if (duration == 0 && this.nullRemovalCause == null) {
    469       // SIZE trumps EXPIRED
    470       this.nullRemovalCause = RemovalCause.EXPIRED;
    471     }
    472     useCustomMap = true;
    473     return this;
    474   }
    475 
    476   private void checkExpiration(long duration, TimeUnit unit) {
    477     checkState(expireAfterWriteNanos == UNSET_INT, "expireAfterWrite was already set to %s ns",
    478         expireAfterWriteNanos);
    479     checkState(expireAfterAccessNanos == UNSET_INT, "expireAfterAccess was already set to %s ns",
    480         expireAfterAccessNanos);
    481     checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit);
    482   }
    483 
    484   long getExpireAfterWriteNanos() {
    485     return (expireAfterWriteNanos == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos;
    486   }
    487 
    488   /**
    489    * Specifies that each entry should be automatically removed from the map once a fixed duration
    490    * has elapsed after the entry's last read or write access.
    491    *
    492    * <p>When {@code duration} is zero, elements can be successfully added to the map, but are
    493    * evicted immediately. This has a very similar effect to invoking {@link #maximumSize
    494    * maximumSize}{@code (0)}. It can be useful in testing, or to disable caching temporarily without
    495    * a code change.
    496    *
    497    * <p>Expired entries may be counted by {@link Map#size}, but will never be visible to read or
    498    * write operations. Expired entries are currently cleaned up during write operations, or during
    499    * occasional read operations in the absense of writes; though this behavior may change in the
    500    * future.
    501    *
    502    * @param duration the length of time after an entry is last accessed that it should be
    503    *     automatically removed
    504    * @param unit the unit that {@code duration} is expressed in
    505    * @throws IllegalArgumentException if {@code duration} is negative
    506    * @throws IllegalStateException if the time to idle or time to live was already set
    507    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    508    *     {@link com.google.common.cache.CacheBuilder}, with {@link #expireAfterAccess} being
    509    *     replaced by {@link com.google.common.cache.CacheBuilder#expireAfterAccess}.
    510    */
    511   @Deprecated
    512   @GwtIncompatible("To be supported")
    513   @Override
    514   MapMaker expireAfterAccess(long duration, TimeUnit unit) {
    515     checkExpiration(duration, unit);
    516     this.expireAfterAccessNanos = unit.toNanos(duration);
    517     if (duration == 0 && this.nullRemovalCause == null) {
    518       // SIZE trumps EXPIRED
    519       this.nullRemovalCause = RemovalCause.EXPIRED;
    520     }
    521     useCustomMap = true;
    522     return this;
    523   }
    524 
    525   long getExpireAfterAccessNanos() {
    526     return (expireAfterAccessNanos == UNSET_INT)
    527         ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos;
    528   }
    529 
    530   Ticker getTicker() {
    531     return firstNonNull(ticker, Ticker.systemTicker());
    532   }
    533 
    534   /**
    535    * Specifies a listener instance, which all maps built using this {@code MapMaker} will notify
    536    * each time an entry is removed from the map by any means.
    537    *
    538    * <p>Each map built by this map maker after this method is called invokes the supplied listener
    539    * after removing an element for any reason (see removal causes in {@link RemovalCause}). It will
    540    * invoke the listener during invocations of any of that map's public methods (even read-only
    541    * methods).
    542    *
    543    * <p><b>Important note:</b> Instead of returning <i>this</i> as a {@code MapMaker} instance,
    544    * this method returns {@code GenericMapMaker<K, V>}. From this point on, either the original
    545    * reference or the returned reference may be used to complete configuration and build the map,
    546    * but only the "generic" one is type-safe. That is, it will properly prevent you from building
    547    * maps whose key or value types are incompatible with the types accepted by the listener already
    548    * provided; the {@code MapMaker} type cannot do this. For best results, simply use the standard
    549    * method-chaining idiom, as illustrated in the documentation at top, configuring a {@code
    550    * MapMaker} and building your {@link Map} all in a single statement.
    551    *
    552    * <p><b>Warning:</b> if you ignore the above advice, and use this {@code MapMaker} to build a map
    553    * or cache whose key or value type is incompatible with the listener, you will likely experience
    554    * a {@link ClassCastException} at some <i>undefined</i> point in the future.
    555    *
    556    * @throws IllegalStateException if a removal listener was already set
    557    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    558    *     {@link com.google.common.cache.CacheBuilder}, with {@link #removalListener} being
    559    *     replaced by {@link com.google.common.cache.CacheBuilder#removalListener}.
    560    */
    561   @Deprecated
    562   @GwtIncompatible("To be supported")
    563   <K, V> GenericMapMaker<K, V> removalListener(RemovalListener<K, V> listener) {
    564     checkState(this.removalListener == null);
    565 
    566     // safely limiting the kinds of maps this can produce
    567     @SuppressWarnings("unchecked")
    568     GenericMapMaker<K, V> me = (GenericMapMaker<K, V>) this;
    569     me.removalListener = checkNotNull(listener);
    570     useCustomMap = true;
    571     return me;
    572   }
    573 
    574   /**
    575    * Builds a thread-safe map, without on-demand computation of values. This method does not alter
    576    * the state of this {@code MapMaker} instance, so it can be invoked again to create multiple
    577    * independent maps.
    578    *
    579    * <p>The bulk operations {@code putAll}, {@code equals}, and {@code clear} are not guaranteed to
    580    * be performed atomically on the returned map. Additionally, {@code size} and {@code
    581    * containsValue} are implemented as bulk read operations, and thus may fail to observe concurrent
    582    * writes.
    583    *
    584    * @return a serializable concurrent map having the requested features
    585    */
    586   @Override
    587   public <K, V> ConcurrentMap<K, V> makeMap() {
    588     if (!useCustomMap) {
    589       return new ConcurrentHashMap<K, V>(getInitialCapacity(), 0.75f, getConcurrencyLevel());
    590     }
    591     return (nullRemovalCause == null)
    592         ? new MapMakerInternalMap<K, V>(this)
    593         : new NullConcurrentMap<K, V>(this);
    594   }
    595 
    596   /**
    597    * Returns a MapMakerInternalMap for the benefit of internal callers that use features of
    598    * that class not exposed through ConcurrentMap.
    599    */
    600   @Override
    601   @GwtIncompatible("MapMakerInternalMap")
    602   <K, V> MapMakerInternalMap<K, V> makeCustomMap() {
    603     return new MapMakerInternalMap<K, V>(this);
    604   }
    605 
    606   /**
    607    * Builds a map that supports atomic, on-demand computation of values. {@link Map#get} either
    608    * returns an already-computed value for the given key, atomically computes it using the supplied
    609    * function, or, if another thread is currently computing the value for this key, simply waits for
    610    * that thread to finish and returns its computed value. Note that the function may be executed
    611    * concurrently by multiple threads, but only for distinct keys.
    612    *
    613    * <p>New code should use {@link com.google.common.cache.CacheBuilder}, which supports
    614    * {@linkplain com.google.common.cache.CacheStats statistics} collection, introduces the
    615    * {@link com.google.common.cache.CacheLoader} interface for loading entries into the cache
    616    * (allowing checked exceptions to be thrown in the process), and more cleanly separates
    617    * computation from the cache's {@code Map} view.
    618    *
    619    * <p>If an entry's value has not finished computing yet, query methods besides {@code get} return
    620    * immediately as if an entry doesn't exist. In other words, an entry isn't externally visible
    621    * until the value's computation completes.
    622    *
    623    * <p>{@link Map#get} on the returned map will never return {@code null}. It may throw:
    624    *
    625    * <ul>
    626    * <li>{@link NullPointerException} if the key is null or the computing function returns a null
    627    *     result
    628    * <li>{@link ComputationException} if an exception was thrown by the computing function. If that
    629    * exception is already of type {@link ComputationException} it is propagated directly; otherwise
    630    * it is wrapped.
    631    * </ul>
    632    *
    633    * <p><b>Note:</b> Callers of {@code get} <i>must</i> ensure that the key argument is of type
    634    * {@code K}. The {@code get} method accepts {@code Object}, so the key type is not checked at
    635    * compile time. Passing an object of a type other than {@code K} can result in that object being
    636    * unsafely passed to the computing function as type {@code K}, and unsafely stored in the map.
    637    *
    638    * <p>If {@link Map#put} is called before a computation completes, other threads waiting on the
    639    * computation will wake up and return the stored value.
    640    *
    641    * <p>This method does not alter the state of this {@code MapMaker} instance, so it can be invoked
    642    * again to create multiple independent maps.
    643    *
    644    * <p>Insertion, removal, update, and access operations on the returned map safely execute
    645    * concurrently by multiple threads. Iterators on the returned map are weakly consistent,
    646    * returning elements reflecting the state of the map at some point at or since the creation of
    647    * the iterator. They do not throw {@link ConcurrentModificationException}, and may proceed
    648    * concurrently with other operations.
    649    *
    650    * <p>The bulk operations {@code putAll}, {@code equals}, and {@code clear} are not guaranteed to
    651    * be performed atomically on the returned map. Additionally, {@code size} and {@code
    652    * containsValue} are implemented as bulk read operations, and thus may fail to observe concurrent
    653    * writes.
    654    *
    655    * @param computingFunction the function used to compute new values
    656    * @return a serializable concurrent map having the requested features
    657    * @deprecated Caching functionality in {@code MapMaker} is being moved to
    658    *     {@link com.google.common.cache.CacheBuilder}, with {@link #makeComputingMap} being replaced
    659    *     by {@link com.google.common.cache.CacheBuilder#build}. Note that uses of
    660    *     {@link #makeComputingMap} with {@code AtomicLong} values can often be migrated to
    661    *     {@link AtomicLongMap}.
    662    *     <b>This method is scheduled for deletion in February 2013.</b>
    663    *
    664    */
    665   @Deprecated
    666   @Override
    667   public <K, V> ConcurrentMap<K, V> makeComputingMap(
    668       Function<? super K, ? extends V> computingFunction) {
    669     return useNullMap()
    670         ? new MapMaker.ComputingMapAdapter<K, V>(this, computingFunction)
    671         : new NullComputingConcurrentMap<K, V>(this, computingFunction);
    672   }
    673 
    674   /**
    675    * Returns a string representation for this MapMaker instance. The exact form of the returned
    676    * string is not specificed.
    677    */
    678   @Override
    679   public String toString() {
    680     Objects.ToStringHelper s = Objects.toStringHelper(this);
    681     if (initialCapacity != UNSET_INT) {
    682       s.add("initialCapacity", initialCapacity);
    683     }
    684     if (concurrencyLevel != UNSET_INT) {
    685       s.add("concurrencyLevel", concurrencyLevel);
    686     }
    687     if (maximumSize != UNSET_INT) {
    688       s.add("maximumSize", maximumSize);
    689     }
    690     if (expireAfterWriteNanos != UNSET_INT) {
    691       s.add("expireAfterWrite", expireAfterWriteNanos + "ns");
    692     }
    693     if (expireAfterAccessNanos != UNSET_INT) {
    694       s.add("expireAfterAccess", expireAfterAccessNanos + "ns");
    695     }
    696     if (keyStrength != null) {
    697       s.add("keyStrength", Ascii.toLowerCase(keyStrength.toString()));
    698     }
    699     if (valueStrength != null) {
    700       s.add("valueStrength", Ascii.toLowerCase(valueStrength.toString()));
    701     }
    702     if (keyEquivalence != null) {
    703       s.addValue("keyEquivalence");
    704     }
    705     if (valueEquivalence != null) {
    706       s.addValue("valueEquivalence");
    707     }
    708     if (removalListener != null) {
    709       s.addValue("removalListener");
    710     }
    711     return s.toString();
    712   }
    713 
    714   /**
    715    * An object that can receive a notification when an entry is removed from a map. The removal
    716    * resulting in notification could have occured to an entry being manually removed or replaced, or
    717    * due to eviction resulting from timed expiration, exceeding a maximum size, or garbage
    718    * collection.
    719    *
    720    * <p>An instance may be called concurrently by multiple threads to process different entries.
    721    * Implementations of this interface should avoid performing blocking calls or synchronizing on
    722    * shared resources.
    723    *
    724    * @param <K> the most general type of keys this listener can listen for; for
    725    *     example {@code Object} if any key is acceptable
    726    * @param <V> the most general type of values this listener can listen for; for
    727    *     example {@code Object} if any key is acceptable
    728    */
    729   interface RemovalListener<K, V> {
    730     /**
    731      * Notifies the listener that a removal occurred at some point in the past.
    732      */
    733     void onRemoval(RemovalNotification<K, V> notification);
    734   }
    735 
    736   /**
    737    * A notification of the removal of a single entry. The key or value may be null if it was already
    738    * garbage collected.
    739    *
    740    * <p>Like other {@code Map.Entry} instances associated with MapMaker, this class holds strong
    741    * references to the key and value, regardless of the type of references the map may be using.
    742    */
    743   static final class RemovalNotification<K, V> extends ImmutableEntry<K, V> {
    744     private static final long serialVersionUID = 0;
    745 
    746     private final RemovalCause cause;
    747 
    748     RemovalNotification(@Nullable K key, @Nullable V value, RemovalCause cause) {
    749       super(key, value);
    750       this.cause = cause;
    751     }
    752 
    753     /**
    754      * Returns the cause for which the entry was removed.
    755      */
    756     public RemovalCause getCause() {
    757       return cause;
    758     }
    759 
    760     /**
    761      * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
    762      * {@link RemovalCause#EXPLICIT} nor {@link RemovalCause#REPLACED}).
    763      */
    764     public boolean wasEvicted() {
    765       return cause.wasEvicted();
    766     }
    767   }
    768 
    769   /**
    770    * The reason why an entry was removed.
    771    */
    772   enum RemovalCause {
    773     /**
    774      * The entry was manually removed by the user. This can result from the user invoking
    775      * {@link Map#remove}, {@link ConcurrentMap#remove}, or {@link java.util.Iterator#remove}.
    776      */
    777     EXPLICIT {
    778       @Override
    779       boolean wasEvicted() {
    780         return false;
    781       }
    782     },
    783 
    784     /**
    785      * The entry itself was not actually removed, but its value was replaced by the user. This can
    786      * result from the user invoking {@link Map#put}, {@link Map#putAll},
    787      * {@link ConcurrentMap#replace(Object, Object)}, or
    788      * {@link ConcurrentMap#replace(Object, Object, Object)}.
    789      */
    790     REPLACED {
    791       @Override
    792       boolean wasEvicted() {
    793         return false;
    794       }
    795     },
    796 
    797     /**
    798      * The entry was removed automatically because its key or value was garbage-collected. This
    799      * can occur when using {@link #softKeys}, {@link #softValues}, {@link #weakKeys}, or {@link
    800      * #weakValues}.
    801      */
    802     COLLECTED {
    803       @Override
    804       boolean wasEvicted() {
    805         return true;
    806       }
    807     },
    808 
    809     /**
    810      * The entry's expiration timestamp has passed. This can occur when using {@link
    811      * #expireAfterWrite} or {@link #expireAfterAccess}.
    812      */
    813     EXPIRED {
    814       @Override
    815       boolean wasEvicted() {
    816         return true;
    817       }
    818     },
    819 
    820     /**
    821      * The entry was evicted due to size constraints. This can occur when using {@link
    822      * #maximumSize}.
    823      */
    824     SIZE {
    825       @Override
    826       boolean wasEvicted() {
    827         return true;
    828       }
    829     };
    830 
    831     /**
    832      * Returns {@code true} if there was an automatic removal due to eviction (the cause is neither
    833      * {@link #EXPLICIT} nor {@link #REPLACED}).
    834      */
    835     abstract boolean wasEvicted();
    836   }
    837 
    838   /** A map that is always empty and evicts on insertion. */
    839   static class NullConcurrentMap<K, V> extends AbstractMap<K, V>
    840       implements ConcurrentMap<K, V>, Serializable {
    841     private static final long serialVersionUID = 0;
    842 
    843     private final RemovalListener<K, V> removalListener;
    844     private final RemovalCause removalCause;
    845 
    846     NullConcurrentMap(MapMaker mapMaker) {
    847       removalListener = mapMaker.getRemovalListener();
    848       removalCause = mapMaker.nullRemovalCause;
    849     }
    850 
    851     // implements ConcurrentMap
    852 
    853     @Override
    854     public boolean containsKey(@Nullable Object key) {
    855       return false;
    856     }
    857 
    858     @Override
    859     public boolean containsValue(@Nullable Object value) {
    860       return false;
    861     }
    862 
    863     @Override
    864     public V get(@Nullable Object key) {
    865       return null;
    866     }
    867 
    868     void notifyRemoval(K key, V value) {
    869       RemovalNotification<K, V> notification =
    870           new RemovalNotification<K, V>(key, value, removalCause);
    871       removalListener.onRemoval(notification);
    872     }
    873 
    874     @Override
    875     public V put(K key, V value) {
    876       checkNotNull(key);
    877       checkNotNull(value);
    878       notifyRemoval(key, value);
    879       return null;
    880     }
    881 
    882     @Override
    883     public V putIfAbsent(K key, V value) {
    884       return put(key, value);
    885     }
    886 
    887     @Override
    888     public V remove(@Nullable Object key) {
    889       return null;
    890     }
    891 
    892     @Override
    893     public boolean remove(@Nullable Object key, @Nullable Object value) {
    894       return false;
    895     }
    896 
    897     @Override
    898     public V replace(K key, V value) {
    899       checkNotNull(key);
    900       checkNotNull(value);
    901       return null;
    902     }
    903 
    904     @Override
    905     public boolean replace(K key, @Nullable V oldValue, V newValue) {
    906       checkNotNull(key);
    907       checkNotNull(newValue);
    908       return false;
    909     }
    910 
    911     @Override
    912     public Set<Entry<K, V>> entrySet() {
    913       return Collections.emptySet();
    914     }
    915   }
    916 
    917   /** Computes on retrieval and evicts the result. */
    918   static final class NullComputingConcurrentMap<K, V> extends NullConcurrentMap<K, V> {
    919     private static final long serialVersionUID = 0;
    920 
    921     final Function<? super K, ? extends V> computingFunction;
    922 
    923     NullComputingConcurrentMap(
    924         MapMaker mapMaker, Function<? super K, ? extends V> computingFunction) {
    925       super(mapMaker);
    926       this.computingFunction = checkNotNull(computingFunction);
    927     }
    928 
    929     @SuppressWarnings("unchecked") // unsafe, which is why Cache is preferred
    930     @Override
    931     public V get(Object k) {
    932       K key = (K) k;
    933       V value = compute(key);
    934       checkNotNull(value, computingFunction + " returned null for key " + key + ".");
    935       notifyRemoval(key, value);
    936       return value;
    937     }
    938 
    939     private V compute(K key) {
    940       checkNotNull(key);
    941       try {
    942         return computingFunction.apply(key);
    943       } catch (ComputationException e) {
    944         throw e;
    945       } catch (Throwable t) {
    946         throw new ComputationException(t);
    947       }
    948     }
    949   }
    950 
    951   /**
    952    * Overrides get() to compute on demand. Also throws an exception when {@code null} is returned
    953    * from a computation.
    954    */
    955   static final class ComputingMapAdapter<K, V>
    956       extends ComputingConcurrentHashMap<K, V> implements Serializable {
    957     private static final long serialVersionUID = 0;
    958 
    959     ComputingMapAdapter(MapMaker mapMaker,
    960         Function<? super K, ? extends V> computingFunction) {
    961       super(mapMaker, computingFunction);
    962     }
    963 
    964     @SuppressWarnings("unchecked") // unsafe, which is one advantage of Cache over Map
    965     @Override
    966     public V get(Object key) {
    967       V value;
    968       try {
    969         value = getOrCompute((K) key);
    970       } catch (ExecutionException e) {
    971         Throwable cause = e.getCause();
    972         Throwables.propagateIfInstanceOf(cause, ComputationException.class);
    973         throw new ComputationException(cause);
    974       }
    975 
    976       if (value == null) {
    977         throw new NullPointerException(computingFunction + " returned null for key " + key + ".");
    978       }
    979       return value;
    980     }
    981   }
    982 
    983 }
    984