001    /*
002     * Copyright (C) 2009 Google Inc.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkArgument;
020    import static com.google.common.base.Preconditions.checkNotNull;
021    import static com.google.common.base.Preconditions.checkState;
022    
023    import com.google.common.annotations.Beta;
024    import com.google.common.annotations.GwtCompatible;
025    import com.google.common.annotations.GwtIncompatible;
026    import com.google.common.base.Equivalence;
027    import com.google.common.base.Function;
028    import com.google.common.base.Objects;
029    import com.google.common.collect.CustomConcurrentHashMap.Strength;
030    
031    import java.io.Serializable;
032    import java.lang.ref.SoftReference;
033    import java.lang.ref.WeakReference;
034    import java.util.Map;
035    import java.util.concurrent.ConcurrentHashMap;
036    import java.util.concurrent.ConcurrentMap;
037    import java.util.concurrent.TimeUnit;
038    
039    /**
040     * <p>A {@link ConcurrentMap} builder, providing any combination of these
041     * features: {@linkplain SoftReference soft} or {@linkplain WeakReference
042     * weak} keys, soft or weak values, size-based evicition, timed expiration, and
043     * on-demand computation of values. Usage example: <pre>   {@code
044     *
045     *   ConcurrentMap<Key, Graph> graphs = new MapMaker()
046     *       .concurrencyLevel(4)
047     *       .softKeys()
048     *       .weakValues()
049     *       .maximumSize(10000)
050     *       .expireAfterWrite(10, TimeUnit.MINUTES)
051     *       .makeComputingMap(
052     *           new Function<Key, Graph>() {
053     *             public Graph apply(Key key) {
054     *               return createExpensiveGraph(key);
055     *             }
056     *           });}</pre>
057     *
058     * These features are all optional; {@code new MapMaker().makeMap()}
059     * returns a valid concurrent map that behaves exactly like a
060     * {@link ConcurrentHashMap}.
061     *
062     * The returned map is implemented as a hash table with similar performance
063     * characteristics to {@link ConcurrentHashMap}. It supports all optional
064     * operations of the {@code ConcurrentMap} interface. It does not permit
065     * null keys or values.
066     *
067     * <p><b>Note:</b> by default, the returned map uses equality comparisons
068     * (the {@link Object#equals(Object) equals} method) to determine equality
069     * for keys or values. However, if {@link #weakKeys()} or {@link
070     * #softKeys()} was specified, the map uses identity ({@code ==})
071     * comparisons instead for keys. Likewise, if {@link #weakValues()} or
072     * {@link #softValues()} was specified, the map uses identity comparisons
073     * for values.
074     *
075     * <p>The returned map has <i>weakly consistent iteration</i>: an iterator
076     * over one of the map's view collections may reflect some, all or none of
077     * the changes made to the map after the iterator was created.
078     *
079     * <p>An entry whose key or value is reclaimed by the garbage collector
080     * immediately disappears from the map. (If the default settings of strong
081     * keys and strong values are used, this will never happen.) The client can
082     * never observe a partially-reclaimed entry. Any {@link java.util.Map.Entry}
083     * instance retrieved from the map's {@linkplain Map#entrySet() entry set}
084     * is a snapshot of that entry's state at the time of retrieval; such entries
085     * do, however, support {@link java.util.Map.Entry#setValue}.
086     *
087     * <p>The maps produced by {@code MapMaker} are serializable, and the
088     * deserialized maps retain all the configuration properties of the original
089     * map. If the map uses soft or weak references, the entries will be
090     * reconstructed as they were, but there is no guarantee that the entries won't
091     * be immediately reclaimed.
092     *
093     * <p>{@code new MapMaker().weakKeys().makeMap()} can almost always be
094     * used as a drop-in replacement for {@link java.util.WeakHashMap}, adding
095     * concurrency, asynchronous cleanup, identity-based equality for keys, and
096     * great flexibility.
097     *
098     * @author Bob Lee
099     * @author Kevin Bourrillion
100     * @since 2 (imported from Google Collections Library)
101     */
102    @GwtCompatible(emulated = true)
103    public final class MapMaker extends GenericMapMaker<Object, Object> {
104      private static final int DEFAULT_INITIAL_CAPACITY = 16;
105      private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
106      private static final int DEFAULT_EXPIRATION_NANOS = 0;
107    
108      static final int UNSET_INT = -1;
109    
110      int initialCapacity = UNSET_INT;
111      int concurrencyLevel = UNSET_INT;
112      int maximumSize = UNSET_INT;
113    
114      Strength keyStrength;
115      Strength valueStrength;
116    
117      long expireAfterWriteNanos = UNSET_INT;
118      long expireAfterAccessNanos = UNSET_INT;
119    
120      // TODO(kevinb): dispense with this after benchmarking
121      boolean useCustomMap;
122    
123      Equivalence<Object> keyEquivalence;
124      Equivalence<Object> valueEquivalence;
125    
126      /**
127       * Constructs a new {@code MapMaker} instance with default settings,
128       * including strong keys, strong values, and no automatic expiration.
129       */
130      public MapMaker() {}
131    
132      // TODO(kevinb): undo this indirection if keyEquiv gets released
133      MapMaker privateKeyEquivalence(Equivalence<Object> equivalence) {
134        checkState(keyEquivalence == null,
135            "key equivalence was already set to %s", keyEquivalence);
136        keyEquivalence = checkNotNull(equivalence);
137        this.useCustomMap = true;
138        return this;
139      }
140    
141      Equivalence<Object> getKeyEquivalence() {
142        return Objects.firstNonNull(keyEquivalence,
143            getKeyStrength().defaultEquivalence());
144      }
145    
146      // TODO(kevinb): undo this indirection if valueEquiv gets released
147      MapMaker privateValueEquivalence(Equivalence<Object> equivalence) {
148        checkState(valueEquivalence == null,
149            "value equivalence was already set to %s", valueEquivalence);
150        this.valueEquivalence = checkNotNull(equivalence);
151        this.useCustomMap = true;
152        return this;
153      }
154    
155      Equivalence<Object> getValueEquivalence() {
156        return Objects.firstNonNull(valueEquivalence,
157            getValueStrength().defaultEquivalence());
158      }
159    
160      /**
161       * Sets a custom initial capacity (defaults to 16). Resizing this or
162       * any other kind of hash table is a relatively slow operation, so,
163       * when possible, it is a good idea to provide estimates of expected
164       * table sizes.
165       *
166       * @throws IllegalArgumentException if {@code initialCapacity} is
167       *   negative
168       * @throws IllegalStateException if an initial capacity was already set
169       */
170      @Override
171      public MapMaker initialCapacity(int initialCapacity) {
172        checkState(this.initialCapacity == UNSET_INT,
173            "initial capacity was already set to %s", this.initialCapacity);
174        checkArgument(initialCapacity >= 0);
175        this.initialCapacity = initialCapacity;
176        return this;
177      }
178    
179      int getInitialCapacity() {
180        return (initialCapacity == UNSET_INT)
181            ? DEFAULT_INITIAL_CAPACITY : initialCapacity;
182      }
183    
184      /**
185       * Specifies the maximum number of entries the map may contain. While the
186       * number of entries in the map is not guaranteed to grow to the maximum,
187       * the map will attempt to make the best use of memory without exceeding the
188       * maximum number of entries. As the map size grows close to the maximum,
189       * the map will evict entries that are less likely to be used again. For
190       * example, the map may evict an entry because it hasn't been used recently
191       * or very often.
192       *
193       * @param size the maximum size of the map
194       *
195       * @throws IllegalArgumentException if {@code size} is not greater than zero
196       * @throws IllegalStateException if a maximum size was already set
197       * @since 8
198       */
199      @Beta
200      @GwtIncompatible("To be supported")
201      @Override
202      public MapMaker maximumSize(int size) {
203        checkState(this.maximumSize == UNSET_INT,
204            "maximum size was already set to %s", this.maximumSize);
205        checkArgument(size > 0, "maximum size must be positive");
206        this.maximumSize = size;
207        this.useCustomMap = true;
208        return this;
209      }
210    
211      /**
212       * Guides the allowed concurrency among update operations. Used as a
213       * hint for internal sizing. The table is internally partitioned to try
214       * to permit the indicated number of concurrent updates without
215       * contention.  Because placement in hash tables is essentially random,
216       * the actual concurrency will vary. Ideally, you should choose a value
217       * to accommodate as many threads as will ever concurrently modify the
218       * table. Using a significantly higher value than you need can waste
219       * space and time, and a significantly lower value can lead to thread
220       * contention. But overestimates and underestimates within an order of
221       * magnitude do not usually have much noticeable impact. A value of one
222       * is appropriate when it is known that only one thread will modify and
223       * all others will only read. Defaults to 16.
224       *
225       * @throws IllegalArgumentException if {@code concurrencyLevel} is
226       *     nonpositive
227       * @throws IllegalStateException if a concurrency level was already set
228       */
229      @GwtIncompatible("java.util.concurrent.ConcurrentHashMap concurrencyLevel")
230      @Override
231      public MapMaker concurrencyLevel(int concurrencyLevel) {
232        checkState(this.concurrencyLevel == UNSET_INT,
233            "concurrency level was already set to %s", this.concurrencyLevel);
234        checkArgument(concurrencyLevel > 0);
235        this.concurrencyLevel = concurrencyLevel;
236        return this;
237      }
238    
239      int getConcurrencyLevel() {
240        return (concurrencyLevel == UNSET_INT)
241            ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel;
242      }
243    
244      /**
245       * Specifies that each key (not value) stored in the map should be
246       * wrapped in a {@link WeakReference} (by default, strong references
247       * are used).
248       *
249       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
250       * to determine equality of weak keys, which may not behave as you expect.
251       * For example, storing a key in the map and then attempting a lookup
252       * using a different but {@link Object#equals(Object) equals}-equivalent
253       * key will always fail.
254       *
255       * @throws IllegalStateException if the key strength was already set
256       * @see WeakReference
257       */
258      @GwtIncompatible("java.lang.ref.WeakReference")
259      @Override
260      public MapMaker weakKeys() {
261        return setKeyStrength(Strength.WEAK);
262      }
263    
264      /**
265       * Specifies that each key (not value) stored in the map should be
266       * wrapped in a {@link SoftReference} (by default, strong references
267       * are used).
268       *
269       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
270       * to determine equality of soft keys, which may not behave as you expect.
271       * For example, storing a key in the map and then attempting a lookup
272       * using a different but {@link Object#equals(Object) equals}-equivalent
273       * key will always fail.
274       *
275       * @throws IllegalStateException if the key strength was already set
276       * @see SoftReference
277       */
278      @GwtIncompatible("java.lang.ref.SoftReference")
279      @Override
280      public MapMaker softKeys() {
281        return setKeyStrength(Strength.SOFT);
282      }
283    
284      MapMaker setKeyStrength(Strength strength) {
285        checkState(keyStrength == null,
286            "Key strength was already set to %s", keyStrength);
287        keyStrength = checkNotNull(strength);
288        if (strength != Strength.STRONG) {
289          // STRONG could be used during deserialization.
290          useCustomMap = true;
291        }
292        return this;
293      }
294    
295      Strength getKeyStrength() {
296        return Objects.firstNonNull(keyStrength, Strength.STRONG);
297      }
298    
299      /**
300       * Specifies that each value (not key) stored in the map should be
301       * wrapped in a {@link WeakReference} (by default, strong references
302       * are used).
303       *
304       * <p>Weak values will be garbage collected once they are weakly
305       * reachable. This makes them a poor candidate for caching; consider
306       * {@link #softValues()} instead.
307       *
308       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
309       * to determine equality of weak values. This will notably impact
310       * the behavior of {@link Map#containsValue(Object) containsValue},
311       * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)},
312       * and {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}.
313       *
314       * @throws IllegalStateException if the value strength was already set
315       * @see WeakReference
316       */
317      @GwtIncompatible("java.lang.ref.WeakReference")
318      @Override
319      public MapMaker weakValues() {
320        return setValueStrength(Strength.WEAK);
321      }
322    
323      /**
324       * Specifies that each value (not key) stored in the map should be
325       * wrapped in a {@link SoftReference} (by default, strong references
326       * are used).
327       *
328       * <p>Soft values will be garbage collected in response to memory
329       * demand, and in a least-recently-used manner. This makes them a
330       * good candidate for caching.
331       *
332       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
333       * to determine equality of soft values. This will notably impact
334       * the behavior of {@link Map#containsValue(Object) containsValue},
335       * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)},
336       * and {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}.
337       *
338       * @throws IllegalStateException if the value strength was already set
339       * @see SoftReference
340       */
341      @GwtIncompatible("java.lang.ref.SoftReference")
342      @Override
343      public MapMaker softValues() {
344        return setValueStrength(Strength.SOFT);
345      }
346    
347      MapMaker setValueStrength(Strength strength) {
348        checkState(valueStrength == null,
349            "Value strength was already set to %s", valueStrength);
350        valueStrength = checkNotNull(strength);
351        if (strength != Strength.STRONG) {
352          // STRONG could be used during deserialization.
353          useCustomMap = true;
354        }
355        return this;
356      }
357    
358      Strength getValueStrength() {
359        return Objects.firstNonNull(valueStrength, Strength.STRONG);
360      }
361    
362      /**
363       * Old name of {@link #expireAfterWrite}.
364       *
365       * @deprecated use {@link #expireAfterWrite}, which behaves exactly the same.
366       *     <b>This method is scheduled for deletion in July 2012.</b>
367       */
368      @Deprecated
369      @Override
370      public MapMaker expiration(long duration, TimeUnit unit) {
371        return expireAfterWrite(duration, unit);
372      }
373    
374      /**
375       * Specifies that each entry should be automatically removed from the
376       * map once a fixed duration has passed since the entry's creation.
377       * Note that changing the value of an entry will reset its expiration
378       * time.
379       *
380       * @param duration the length of time after an entry is created that it
381       *     should be automatically removed
382       * @param unit the unit that {@code duration} is expressed in
383       * @throws IllegalArgumentException if {@code duration} is not positive, or is
384       *     larger than one hundred years
385       * @throws IllegalStateException if the time to live or time to idle was
386       *     already set
387       * @since 8
388       */
389      @Beta
390      @Override
391      public MapMaker expireAfterWrite(long duration, TimeUnit unit) {
392        checkExpiration(duration, unit);
393        this.expireAfterWriteNanos = unit.toNanos(duration);
394        useCustomMap = true;
395        return this;
396      }
397    
398      private void checkExpiration(long duration, TimeUnit unit) {
399        checkState(expireAfterWriteNanos == UNSET_INT,
400            "expireAfterWrite was already set to %s ns",
401            expireAfterWriteNanos);
402        checkState(expireAfterAccessNanos == UNSET_INT,
403            "expireAfterAccess was already set to %s ns",
404            expireAfterAccessNanos);
405        checkArgument(duration > 0, "duration must be positive: %s %s",
406            duration, unit);
407      }
408    
409      long getExpireAfterWriteNanos() {
410        return (expireAfterWriteNanos == UNSET_INT)
411            ? DEFAULT_EXPIRATION_NANOS : expireAfterWriteNanos;
412      }
413    
414      /**
415       * Specifies that each entry should be automatically removed from the
416       * map once a fixed duration has passed since the entry's last access.
417       *
418       * @param duration the length of time after an entry is last accessed
419       *     that it should be automatically removed
420       * @param unit the unit that {@code duration} is expressed in
421       * @throws IllegalArgumentException if {@code duration} is not positive, or is
422       *     larger than one hundred years
423       * @throws IllegalStateException if the time to idle or time to live was
424       *     already set
425       * @since 8
426       */
427      @Beta
428      @GwtIncompatible("To be supported")
429      @Override
430      public MapMaker expireAfterAccess(long duration, TimeUnit unit) {
431        checkExpiration(duration, unit);
432        this.expireAfterAccessNanos = unit.toNanos(duration);
433        useCustomMap = true;
434        return this;
435      }
436    
437      long getExpireAfterAccessNanos() {
438        return (expireAfterAccessNanos == UNSET_INT)
439            ? DEFAULT_EXPIRATION_NANOS : expireAfterAccessNanos;
440      }
441    
442      /**
443       * Specifies a listener instance, which all maps built using this {@code
444       * MapMaker} will notify each time an entry is evicted.
445       *
446       * <p>A map built by this map maker will invoke the supplied listener after it
447       * evicts an entry, whether it does so due to timed expiration, exceeding the
448       * maximum size, or discovering that the key or value has been reclaimed by
449       * the garbage collector. It will invoke the listener synchronously, during
450       * invocations of any of that map's public methods (even read-only methods).
451       * The listener will <i>not</i> be invoked on manual removal.
452       *
453       * <p><b>Important note:</b> Instead of returning <em>this</em> as a {@code
454       * MapMaker} instance, this method returns {@code GenericMapMaker<K, V>}.
455       * From this point on, either the original reference or the returned
456       * reference may be used to complete configuration and build the map, but only
457       * the "generic" one is type-safe. That is, it will properly prevent you from
458       * building maps whose key or value types are incompatible with the types
459       * accepted by the listener already provided; the {@code MapMaker} type cannot
460       * do this. For best results, simply use the standard method-chaining idiom,
461       * as illustrated in the documentation at top, configuring a {@code MapMaker}
462       * and building your {@link Map} all in a single statement.
463       *
464       * <p><b>Warning:</b> if you ignore the above advice, and use this {@code
465       * MapMaker} to build maps whose key or value types are incompatible with the
466       * listener, you will likely experience a {@link ClassCastException} at an
467       * undefined point in the future.
468       *
469       * @throws IllegalStateException if an eviction listener was already set
470       * @since 7
471       */
472      @Beta
473      @GwtIncompatible("To be supported")
474      public <K, V> GenericMapMaker<K, V> evictionListener(
475          MapEvictionListener<K, V> listener) {
476        checkState(this.evictionListener == null);
477    
478        // safely limiting the kinds of maps this can produce
479        @SuppressWarnings("unchecked")
480        GenericMapMaker<K, V> me = (GenericMapMaker<K, V>) this;
481        me.evictionListener = checkNotNull(listener);
482        useCustomMap = true;
483        return me;
484      }
485    
486      /**
487       * Builds a map, without on-demand computation of values. This method
488       * does not alter the state of this {@code MapMaker} instance, so it can be
489       * invoked again to create multiple independent maps.
490       *
491       * @return a serializable concurrent map having the requested features
492       */
493      @Override
494      public <K, V> ConcurrentMap<K, V> makeMap() {
495        return useCustomMap
496            ? new CustomConcurrentHashMap<K, V>(this)
497            : new ConcurrentHashMap<K, V>(getInitialCapacity(),
498                0.75f, getConcurrencyLevel());
499      }
500    
501      /**
502       * Builds a caching function, which either returns an already-computed value
503       * for a given key or atomically computes it using the supplied function.
504       * If another thread is currently computing the value for this key, simply
505       * waits for that thread to finish and returns its computed value. Note that
506       * the function may be executed concurrently by multiple threads, but only for
507       * distinct keys.
508       *
509       * <p>The {@code Map} view of the {@code Cache}'s cache is only
510       * updated when function computation completes. In other words, an entry isn't
511       * visible until the value's computation completes. No methods on the {@code
512       * Map} will ever trigger computation.
513       *
514       * <p>{@link Cache#apply} in the returned function implementation may
515       * throw:
516       *
517       * <ul>
518       * <li>{@link NullPointerException} if the key is null or the
519       *     computing function returns null
520       * <li>{@link ComputationException} if an exception was thrown by the
521       *     computing function. If that exception is already of type {@link
522       *     ComputationException} it is propagated directly; otherwise it is
523       *     wrapped.
524       * </ul>
525       *
526       * <p>If {@link Map#put} is called before a computation completes, other
527       * threads waiting on the computation will wake up and return the stored
528       * value. When the computation completes, its result will be ignored.
529       *
530       * <p>This method does not alter the state of this {@code MapMaker} instance,
531       * so it can be invoked again to create multiple independent maps.
532       *
533       * @param computingFunction the function used to compute new values
534       * @return a serializable cache having the requested features
535       */
536      // TODO(kevinb): figure out the Cache interface before making this public
537      <K, V> Cache<K, V> makeCache(
538          Function<? super K, ? extends V> computingFunction) {
539        return new ComputingConcurrentHashMap<K, V>(this, computingFunction);
540      }
541    
542      /**
543       * Builds a map that supports atomic, on-demand computation of values. {@link
544       * Map#get} either returns an already-computed value for the given key,
545       * atomically computes it using the supplied function, or, if another thread
546       * is currently computing the value for this key, simply waits for that thread
547       * to finish and returns its computed value. Note that the function may be
548       * executed concurrently by multiple threads, but only for distinct keys.
549       *
550       * <p>If an entry's value has not finished computing yet, query methods
551       * besides {@code get} return immediately as if an entry doesn't exist. In
552       * other words, an entry isn't externally visible until the value's
553       * computation completes.
554       *
555       * <p>{@link Map#get} on the returned map will never return {@code null}. It
556       * may throw:
557       *
558       * <ul>
559       * <li>{@link NullPointerException} if the key is null or the computing
560       *     function returns null
561       * <li>{@link ComputationException} if an exception was thrown by the
562       *     computing function. If that exception is already of type {@link
563       *     ComputationException} it is propagated directly; otherwise it is
564       *     wrapped.
565       * </ul>
566       *
567       * <p><b>Note:</b> Callers of {@code get} <i>must</i> ensure that the key
568       * argument is of type {@code K}. The {@code get} method accepts {@code
569       * Object}, so the key type is not checked at compile time. Passing an object
570       * of a type other than {@code K} can result in that object being unsafely
571       * passed to the computing function as type {@code K}, and unsafely stored in
572       * the map.
573       *
574       * <p>If {@link Map#put} is called before a computation completes, other
575       * threads waiting on the computation will wake up and return the stored
576       * value.
577       *
578       * <p>This method does not alter the state of this {@code MapMaker} instance,
579       * so it can be invoked again to create multiple independent maps.
580       *
581       * @param computingFunction the function used to compute new values
582       * @return a serializable concurrent map having the requested features
583       */
584      @Override
585      public <K, V> ConcurrentMap<K, V> makeComputingMap(
586          Function<? super K, ? extends V> computingFunction) {
587        Cache<K, V> cache = makeCache(computingFunction);
588        return new ComputingMapAdapter<K, V>(cache);
589      }
590    
591      /**
592       * A function which caches the result of each application (computation). This
593       * interface does not specify the caching semantics, but does expose a {@code
594       * ConcurrentMap} view of cached entries.
595       */
596      interface Cache<K, V> extends Function<K, V> {
597    
598        /**
599         * Returns a map view of the cached entries.
600         */
601        ConcurrentMap<K, V> asMap();
602      }
603    
604      /**
605       * Overrides get() to compute on demand.
606       */
607      static class ComputingMapAdapter<K, V> extends ForwardingConcurrentMap<K, V>
608          implements Serializable {
609        private static final long serialVersionUID = 0;
610    
611        final Cache<K, V> cache;
612    
613        ComputingMapAdapter(Cache<K, V> cache) {
614          this.cache = cache;
615        }
616    
617        @Override protected ConcurrentMap<K, V> delegate() {
618          return cache.asMap();
619        }
620    
621        @SuppressWarnings("unchecked") // unsafe, which is why this is deprecated
622        @Override public V get(Object key) {
623          return cache.apply((K) key);
624        }
625      }
626    }