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.GwtCompatible;
024    import com.google.common.annotations.GwtIncompatible;
025    import com.google.common.base.Equivalence;
026    import com.google.common.base.Function;
027    import com.google.common.base.Objects;
028    import com.google.common.collect.CustomConcurrentHashMap.Strength;
029    
030    import java.io.Serializable;
031    import java.lang.ref.SoftReference;
032    import java.lang.ref.WeakReference;
033    import java.util.Map;
034    import java.util.concurrent.ConcurrentHashMap;
035    import java.util.concurrent.ConcurrentMap;
036    import java.util.concurrent.TimeUnit;
037    
038    /**
039     * <p>A {@link ConcurrentMap} builder, providing any combination of these
040     * features: {@linkplain SoftReference soft} or {@linkplain WeakReference
041     * weak} keys, soft or weak values, timed expiration, and on-demand
042     * computation of values. Usage example: <pre> {@code
043     *
044     *   ConcurrentMap<Key, Graph> graphs = new MapMaker()
045     *       .concurrencyLevel(32)
046     *       .softKeys()
047     *       .weakValues()
048     *       .expiration(30, TimeUnit.MINUTES)
049     *       .makeComputingMap(
050     *           new Function<Key, Graph>() {
051     *             public Graph apply(Key key) {
052     *               return createExpensiveGraph(key);
053     *             }
054     *           });}</pre>
055     *
056     * These features are all optional; {@code new MapMaker().makeMap()}
057     * returns a valid concurrent map that behaves exactly like a
058     * {@link ConcurrentHashMap}.
059     *
060     * The returned map is implemented as a hash table with similar performance
061     * characteristics to {@link ConcurrentHashMap}. It supports all optional
062     * operations of the {@code ConcurrentMap} interface. It does not permit
063     * null keys or values. It is serializable; however, serializing a map that
064     * uses soft or weak references can give unpredictable results.
065     *
066     * <p><b>Note:</b> by default, the returned map uses equality comparisons
067     * (the {@link Object#equals(Object) equals} method) to determine equality
068     * for keys or values. However, if {@link #weakKeys()} or {@link
069     * #softKeys()} was specified, the map uses identity ({@code ==})
070     * comparisons instead for keys. Likewise, if {@link #weakValues()} or
071     * {@link #softValues()} was specified, the map uses identity comparisons
072     * for values.
073     *
074     * <p>The returned map has <i>weakly consistent iteration</i>: an iterator
075     * over one of the map's view collections may reflect some, all or none of
076     * the changes made to the map after the iterator was created.
077     *
078     * <p>An entry whose key or value is reclaimed by the garbage collector
079     * immediately disappears from the map. (If the default settings of strong
080     * keys and strong values are used, this will never happen.) The client can
081     * never observe a partially-reclaimed entry. Any {@link java.util.Map.Entry}
082     * instance retrieved from the map's {@linkplain Map#entrySet() entry set}
083     * is a snapshot of that entry's state at the time of retrieval; such entries
084     * do, however, support {@link java.util.Map.Entry#setValue}.
085     *
086     * <p>{@code new MapMaker().weakKeys().makeMap()} can almost always be
087     * used as a drop-in replacement for {@link java.util.WeakHashMap}, adding
088     * concurrency, asynchronous cleanup, identity-based equality for keys, and
089     * great flexibility.
090     *
091     * @author Bob Lee
092     * @author Kevin Bourrillion
093     * @since 2 (imported from Google Collections Library)
094     */
095    @GwtCompatible(emulated = true)
096    public final class MapMaker {
097      private static final int DEFAULT_INITIAL_CAPACITY = 16;
098      private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
099      private static final int DEFAULT_EXPIRATION_NANOS = 0;
100    
101      private static final int UNSET_INITIAL_CAPACITY = -1;
102      private static final int UNSET_CONCURRENCY_LEVEL = -1;
103      static final int UNSET_EXPIRATION_NANOS = -1;
104      static final int UNSET_MAXIMUM_SIZE = -1;
105    
106      int initialCapacity = UNSET_INITIAL_CAPACITY;
107      int concurrencyLevel = UNSET_CONCURRENCY_LEVEL;
108      int maximumSize = UNSET_MAXIMUM_SIZE;
109    
110      Strength keyStrength;
111      Strength valueStrength;
112    
113      long expirationNanos = UNSET_EXPIRATION_NANOS;
114    
115      private boolean useCustomMap;
116    
117      Equivalence<Object> keyEquivalence;
118      Equivalence<Object> valueEquivalence;
119    
120      /**
121       * Constructs a new {@code MapMaker} instance with default settings,
122       * including strong keys, strong values, and no automatic expiration.
123       */
124      public MapMaker() {}
125    
126      // TODO: undo this indirection if keyEquiv gets released
127      MapMaker privateKeyEquivalence(Equivalence<Object> equivalence) {
128        checkState(keyEquivalence == null,
129            "key equivalence was already set to " + keyEquivalence);
130        keyEquivalence = checkNotNull(equivalence);
131        this.useCustomMap = true;
132        return this;
133      }
134    
135      Equivalence<Object> getKeyEquivalence() {
136        return Objects.firstNonNull(keyEquivalence,
137            getKeyStrength().defaultEquivalence());
138      }
139    
140      // TODO: undo this indirection if valueEquiv gets released
141      MapMaker privateValueEquivalence(Equivalence<Object> equivalence) {
142        checkState(valueEquivalence == null,
143            "value equivalence was already set to " + valueEquivalence);
144        this.valueEquivalence = checkNotNull(equivalence);
145        this.useCustomMap = true;
146        return this;
147      }
148    
149      Equivalence<Object> getValueEquivalence() {
150        return Objects.firstNonNull(valueEquivalence,
151            getValueStrength().defaultEquivalence());
152      }
153    
154      /**
155       * Sets a custom initial capacity (defaults to 16). Resizing this or
156       * any other kind of hash table is a relatively slow operation, so,
157       * when possible, it is a good idea to provide estimates of expected
158       * table sizes.
159       *
160       * @throws IllegalArgumentException if {@code initialCapacity} is
161       *   negative
162       * @throws IllegalStateException if an initial capacity was already set
163       */
164      public MapMaker initialCapacity(int initialCapacity) {
165        checkState(this.initialCapacity == UNSET_INITIAL_CAPACITY,
166            "initial capacity was already set to " + this.initialCapacity);
167        checkArgument(initialCapacity >= 0);
168        this.initialCapacity = initialCapacity;
169        return this;
170      }
171    
172      int getInitialCapacity() {
173        return (initialCapacity == UNSET_INITIAL_CAPACITY)
174            ? DEFAULT_INITIAL_CAPACITY : initialCapacity;
175      }
176    
177      /**
178       * Specifies the maximum number of entries the map may contain. While the
179       * number of entries in the map is not guaranteed to grow to the maximum,
180       * the map will attempt to make the best use of memory without exceeding the
181       * maximum number of entries. As the map size grows close to the maximum,
182       * the map will evict entries that are less likely to be used again. For
183       * example, the map may evict an entry because it hasn't been used recently
184       * or very often.
185       *
186       * @throws IllegalArgumentException if {@code size} is negative
187       * @throws IllegalStateException if a maximum size was already set
188       */
189      // TODO: Implement and make public.
190      MapMaker maximumSize(int size) {
191        // TODO: Should we disallow maximumSize < concurrencyLevel? If we allow it,
192        // should we return a dummy map that doesn't actually retain any
193        // entries?
194    
195        checkState(this.maximumSize == UNSET_MAXIMUM_SIZE,
196            "maximum size was already set to " + this.maximumSize);
197        checkArgument(initialCapacity >= 0);
198        this.maximumSize = size;
199        this.useCustomMap = true;
200        return this;
201      }
202    
203      /**
204       * Guides the allowed concurrency among update operations. Used as a
205       * hint for internal sizing. The table is internally partitioned to try
206       * to permit the indicated number of concurrent updates without
207       * contention.  Because placement in hash tables is essentially random,
208       * the actual concurrency will vary. Ideally, you should choose a value
209       * to accommodate as many threads as will ever concurrently modify the
210       * table. Using a significantly higher value than you need can waste
211       * space and time, and a significantly lower value can lead to thread
212       * contention. But overestimates and underestimates within an order of
213       * magnitude do not usually have much noticeable impact. A value of one
214       * is appropriate when it is known that only one thread will modify and
215       * all others will only read. Defaults to 16.
216       *
217       * @throws IllegalArgumentException if {@code concurrencyLevel} is
218       *     nonpositive
219       * @throws IllegalStateException if a concurrency level was already set
220       */
221      @GwtIncompatible("java.util.concurrent.ConcurrentHashMap concurrencyLevel")
222      public MapMaker concurrencyLevel(int concurrencyLevel) {
223        checkState(this.concurrencyLevel == UNSET_CONCURRENCY_LEVEL,
224            "concurrency level was already set to " + this.concurrencyLevel);
225        checkArgument(concurrencyLevel > 0);
226        this.concurrencyLevel = concurrencyLevel;
227        return this;
228      }
229    
230      int getConcurrencyLevel() {
231        return (concurrencyLevel == UNSET_CONCURRENCY_LEVEL)
232            ? DEFAULT_CONCURRENCY_LEVEL : concurrencyLevel;
233      }
234    
235      /**
236       * Specifies that each key (not value) stored in the map should be
237       * wrapped in a {@link WeakReference} (by default, strong references
238       * are used).
239       *
240       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
241       * to determine equality of weak keys, which may not behave as you expect.
242       * For example, storing a key in the map and then attempting a lookup
243       * using a different but {@link Object#equals(Object) equals}-equivalent
244       * key will always fail.
245       *
246       * @throws IllegalStateException if the key strength was already set
247       * @see WeakReference
248       */
249      @GwtIncompatible("java.lang.ref.WeakReference")
250      public MapMaker weakKeys() {
251        return setKeyStrength(Strength.WEAK);
252      }
253    
254      /**
255       * Specifies that each key (not value) stored in the map should be
256       * wrapped in a {@link SoftReference} (by default, strong references
257       * are used).
258       *
259       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
260       * to determine equality of soft keys, which may not behave as you expect.
261       * For example, storing a key in the map and then attempting a lookup
262       * using a different but {@link Object#equals(Object) equals}-equivalent
263       * key will always fail.
264       *
265       * @throws IllegalStateException if the key strength was already set
266       * @see SoftReference
267       */
268      @GwtIncompatible("java.lang.ref.SoftReference")
269      public MapMaker softKeys() {
270        return setKeyStrength(Strength.SOFT);
271      }
272    
273      MapMaker setKeyStrength(Strength strength) {
274        checkState(keyStrength == null,
275            "Key strength was already set to " + keyStrength + ".");
276        keyStrength = checkNotNull(strength);
277        if (strength != Strength.STRONG) {
278          // STRONG could be used during deserialization.
279          useCustomMap = true;
280        }
281        return this;
282      }
283    
284      Strength getKeyStrength() {
285        return Objects.firstNonNull(keyStrength, Strength.STRONG);
286      }
287    
288      /**
289       * Specifies that each value (not key) stored in the map should be
290       * wrapped in a {@link WeakReference} (by default, strong references
291       * are used).
292       *
293       * <p>Weak values will be garbage collected once they are weakly
294       * reachable. This makes them a poor candidate for caching; consider
295       * {@link #softValues()} instead.
296       *
297       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
298       * to determine equality of weak values. This will notably impact
299       * the behavior of {@link Map#containsValue(Object) containsValue},
300       * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)},
301       * and {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}.
302       *
303       * @throws IllegalStateException if the key strength was already set
304       * @see WeakReference
305       */
306      @GwtIncompatible("java.lang.ref.WeakReference")
307      public MapMaker weakValues() {
308        return setValueStrength(Strength.WEAK);
309      }
310    
311      /**
312       * Specifies that each value (not key) stored in the map should be
313       * wrapped in a {@link SoftReference} (by default, strong references
314       * are used).
315       *
316       * <p>Soft values will be garbage collected in response to memory
317       * demand, and in a least-recently-used manner. This makes them a
318       * good candidate for caching.
319       *
320       * <p><b>Note:</b> the map will use identity ({@code ==}) comparison
321       * to determine equality of soft values. This will notably impact
322       * the behavior of {@link Map#containsValue(Object) containsValue},
323       * {@link ConcurrentMap#remove(Object, Object) remove(Object, Object)},
324       * and {@link ConcurrentMap#replace(Object, Object, Object) replace(K, V, V)}.
325       *
326       * @throws IllegalStateException if the value strength was already set
327       * @see SoftReference
328       */
329      @GwtIncompatible("java.lang.ref.SoftReference")
330      public MapMaker softValues() {
331        return setValueStrength(Strength.SOFT);
332      }
333    
334      MapMaker setValueStrength(Strength strength) {
335        checkState(valueStrength == null,
336            "Value strength was already set to " + valueStrength + ".");
337        valueStrength = checkNotNull(strength);
338        if (strength != Strength.STRONG) {
339          // STRONG could be used during deserialization.
340          useCustomMap = true;
341        }
342        return this;
343      }
344    
345      Strength getValueStrength() {
346        return Objects.firstNonNull(valueStrength, Strength.STRONG);
347      }
348    
349      /**
350       * Specifies that each entry should be automatically removed from the
351       * map once a fixed duration has passed since the entry's creation.
352       * Note that changing the value of an entry will reset its expiration
353       * time.
354       *
355       * @param duration the length of time after an entry is created that it
356       *     should be automatically removed
357       * @param unit the unit that {@code duration} is expressed in
358       * @throws IllegalArgumentException if {@code duration} is not positive
359       * @throws IllegalStateException if the expiration time was already set
360       */
361      public MapMaker expiration(long duration, TimeUnit unit) {
362        checkState(expirationNanos == UNSET_EXPIRATION_NANOS,
363            "expiration time of " + expirationNanos + " ns was already set");
364        checkArgument(duration > 0,
365            "invalid duration: " + duration);
366        this.expirationNanos = unit.toNanos(duration);
367        useCustomMap = true;
368        return this;
369      }
370    
371      long getExpirationNanos() {
372        return (expirationNanos == UNSET_EXPIRATION_NANOS)
373            ? DEFAULT_EXPIRATION_NANOS : expirationNanos;
374      }
375    
376      /**
377       * Builds a map, without on-demand computation of values. This method
378       * does not alter the state of this {@code MapMaker} instance, so it can be
379       * invoked again to create multiple independent maps.
380       *
381       * @param <K> the type of keys to be stored in the returned map
382       * @param <V> the type of values to be stored in the returned map
383       * @return a serializable concurrent map having the requested features
384       */
385      public <K, V> ConcurrentMap<K, V> makeMap() {
386        return useCustomMap
387            ? new CustomConcurrentHashMap<K, V>(this)
388            : new ConcurrentHashMap<K, V>(getInitialCapacity(),
389                0.75f, getConcurrencyLevel());
390      }
391    
392      /**
393       * Builds a caching function, which either returns an already-computed value
394       * for a given key or atomically computes it using the supplied function.
395       * If another thread is currently computing the value for this key, simply
396       * waits for that thread to finish and returns its computed value. Note that
397       * the function may be executed concurrently by multiple threads, but only for
398       * distinct keys.
399       *
400       * <p>The {@code Map} view of the {@code Cache}'s cache is only
401       * updated when function computation completes. In other words, an entry isn't
402       * visible until the value's computation completes. No methods on the {@code
403       * Map} will ever trigger computation.
404       *
405       * <p>{@link Cache#apply} in the returned function implementation may
406       * throw:
407       *
408       * <ul>
409       * <li>{@link NullPointerException} if the key is null or the
410       *     computing function returns null
411       * <li>{@link ComputationException} if an exception was thrown by the
412       *     computing function. If that exception is already of type {@link
413       *     ComputationException} it is propagated directly; otherwise it is
414       *     wrapped.
415       * </ul>
416       *
417       * <p>If {@link Map#put} is called on the underlying map before a computation
418       * completes, other threads waiting on the computation will wake up and return
419       * the stored value. When the computation completes, its new result will
420       * overwrite the value that was put in the map manually.
421       *
422       * <p>This method does not alter the state of this {@code MapMaker} instance,
423       * so it can be invoked again to create multiple independent maps.
424       *
425       * @param <K> the type of keys to be stored in the returned cache
426       * @param <V> the type of values to be stored in the returned cache
427       * @return a serializable cache having the requested features
428       */
429      // TODO: figure out the Cache interface first
430      <K, V> Cache<K, V> makeCache(
431          Function<? super K, ? extends V> computingFunction) {
432        return new ComputingConcurrentHashMap<K, V>(this, computingFunction);
433      }
434    
435      /**
436       * Builds a map that supports atomic, on-demand computation of values. {@link
437       * Map#get} either returns an already-computed value for the given key,
438       * atomically computes it using the supplied function, or, if another thread
439       * is currently computing the value for this key, simply waits for that thread
440       * to finish and returns its computed value. Note that the function may be
441       * executed concurrently by multiple threads, but only for distinct keys.
442       *
443       * <p>If an entry's value has not finished computing yet, query methods
444       * besides {@code get} return immediately as if an entry doesn't exist. In
445       * other words, an entry isn't externally visible until the value's
446       * computation completes.
447       *
448       * <p>{@link Map#get} on the returned map will never return {@code null}. It
449       * may throw:
450       *
451       * <ul>
452       * <li>{@link NullPointerException} if the key is null or the computing
453       *     function returns null
454       * <li>{@link ComputationException} if an exception was thrown by the
455       *     computing function. If that exception is already of type {@link
456       *     ComputationException} it is propagated directly; otherwise it is
457       *     wrapped.
458       * </ul>
459       *
460       * <p><b>Note:</b> Callers of {@code get} <i>must</i> ensure that the key
461       * argument is of type {@code K}. The {@code get} method accepts {@code
462       * Object}, so the key type is not checked at compile time. Passing an object
463       * of a type other than {@code K} can result in that object being unsafely
464       * passed to the computing function as type {@code K}, and unsafely stored in
465       * the map.
466       *
467       * <p>If {@link Map#put} is called before a computation completes, other
468       * threads waiting on the computation will wake up and return the stored
469       * value. When the computation completes, its new result will overwrite the
470       * value that was put in the map manually.
471       *
472       * <p>This method does not alter the state of this {@code MapMaker} instance,
473       * so it can be invoked again to create multiple independent maps.
474       */
475      public <K, V> ConcurrentMap<K, V> makeComputingMap(
476          Function<? super K, ? extends V> computingFunction) {
477        Cache<K, V> cache = makeCache(computingFunction);
478        return new ComputingMapAdapter<K, V>(cache);
479      }
480    
481      /**
482       * A function which caches the result of each application (computation). This
483       * interface does not specify the caching semantics, but does expose a {@code
484       * ConcurrentMap} view of cached entries.
485       *
486       * @author Bob Lee
487       */
488      interface Cache<K, V> extends Function<K, V> {
489    
490        /**
491         * Returns a map view of the cached entries.
492         */
493        ConcurrentMap<K, V> asMap();
494      }
495    
496      /**
497       * Overrides get() to compute on demand.
498       */
499      static class ComputingMapAdapter<K, V> extends ForwardingConcurrentMap<K, V>
500          implements Serializable {
501        private static final long serialVersionUID = 0;
502    
503        final Cache<K, V> cache;
504    
505        ComputingMapAdapter(Cache<K, V> cache) {
506          this.cache = cache;
507        }
508    
509        @Override protected ConcurrentMap<K, V> delegate() {
510          return cache.asMap();
511        }
512    
513        @SuppressWarnings("unchecked") // unsafe, which is why this is deprecated
514        @Override public V get(Object key) {
515          return cache.apply((K) key);
516        }
517      }
518    }