Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2007 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.Preconditions.checkNotNull;
     18 
     19 import com.google.common.annotations.Beta;
     20 import com.google.common.annotations.GwtIncompatible;
     21 import com.google.common.base.Equivalence;
     22 import com.google.common.base.Function;
     23 import com.google.common.collect.MapMakerInternalMap.ReferenceEntry;
     24 
     25 import java.util.concurrent.ConcurrentMap;
     26 
     27 /**
     28  * Contains static methods pertaining to instances of {@link Interner}.
     29  *
     30  * @author Kevin Bourrillion
     31  * @since 3.0
     32  */
     33 @Beta
     34 public final class Interners {
     35   private Interners() {}
     36 
     37   /**
     38    * Returns a new thread-safe interner which retains a strong reference to each instance it has
     39    * interned, thus preventing these instances from being garbage-collected. If this retention is
     40    * acceptable, this implementation may perform better than {@link #newWeakInterner}. Note that
     41    * unlike {@link String#intern}, using this interner does not consume memory in the permanent
     42    * generation.
     43    */
     44   public static <E> Interner<E> newStrongInterner() {
     45     final ConcurrentMap<E, E> map = new MapMaker().makeMap();
     46     return new Interner<E>() {
     47       @Override public E intern(E sample) {
     48         E canonical = map.putIfAbsent(checkNotNull(sample), sample);
     49         return (canonical == null) ? sample : canonical;
     50       }
     51     };
     52   }
     53 
     54   /**
     55    * Returns a new thread-safe interner which retains a weak reference to each instance it has
     56    * interned, and so does not prevent these instances from being garbage-collected. This most
     57    * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative
     58    * when the memory usage of that implementation is unacceptable. Note that unlike {@link
     59    * String#intern}, using this interner does not consume memory in the permanent generation.
     60    */
     61   @GwtIncompatible("java.lang.ref.WeakReference")
     62   public static <E> Interner<E> newWeakInterner() {
     63     return new WeakInterner<E>();
     64   }
     65 
     66   private static class WeakInterner<E> implements Interner<E> {
     67     // MapMaker is our friend, we know about this type
     68     private final MapMakerInternalMap<E, Dummy> map = new MapMaker()
     69           .weakKeys()
     70           .keyEquivalence(Equivalence.equals())
     71           .makeCustomMap();
     72 
     73     @Override public E intern(E sample) {
     74       while (true) {
     75         // trying to read the canonical...
     76         ReferenceEntry<E, Dummy> entry = map.getEntry(sample);
     77         if (entry != null) {
     78           E canonical = entry.getKey();
     79           if (canonical != null) { // only matters if weak/soft keys are used
     80             return canonical;
     81           }
     82         }
     83 
     84         // didn't see it, trying to put it instead...
     85         Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE);
     86         if (sneaky == null) {
     87           return sample;
     88         } else {
     89           /* Someone beat us to it! Trying again...
     90            *
     91            * Technically this loop not guaranteed to terminate, so theoretically (extremely
     92            * unlikely) this thread might starve, but even then, there is always going to be another
     93            * thread doing progress here.
     94            */
     95         }
     96       }
     97     }
     98 
     99     private enum Dummy { VALUE }
    100   }
    101 
    102   /**
    103    * Returns a function that delegates to the {@link Interner#intern} method of the given interner.
    104    *
    105    * @since 8.0
    106    */
    107   public static <E> Function<E, E> asFunction(Interner<E> interner) {
    108     return new InternerFunction<E>(checkNotNull(interner));
    109   }
    110 
    111   private static class InternerFunction<E> implements Function<E, E> {
    112 
    113     private final Interner<E> interner;
    114 
    115     public InternerFunction(Interner<E> interner) {
    116       this.interner = interner;
    117     }
    118 
    119     @Override public E apply(E input) {
    120       return interner.intern(input);
    121     }
    122 
    123     @Override public int hashCode() {
    124       return interner.hashCode();
    125     }
    126 
    127     @Override public boolean equals(Object other) {
    128       if (other instanceof InternerFunction) {
    129         InternerFunction<?> that = (InternerFunction<?>) other;
    130         return interner.equals(that.interner);
    131       }
    132 
    133       return false;
    134     }
    135   }
    136 }
    137