View Javadoc
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.annotations.VisibleForTesting;
22  import com.google.common.base.Equivalence;
23  import com.google.common.base.Function;
24  import com.google.common.collect.MapMaker.Dummy;
25  import com.google.common.collect.MapMakerInternalMap.InternalEntry;
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  @GwtIncompatible
35  public final class Interners {
36    private Interners() {}
37  
38    /**
39     * Builder for {@link Interner} instances.
40     *
41     * @since 21.0
42     */
43    public static class InternerBuilder {
44      private final MapMaker mapMaker = new MapMaker();
45      private boolean strong = true;
46  
47      private InternerBuilder() {
48      }
49  
50      /**
51       * Instructs the {@link InternerBuilder} to build a strong interner.
52       *
53       * @see Interners#newStrongInterner()
54       */
55      public InternerBuilder strong() {
56        this.strong = true;
57        return this;
58      }
59  
60      /**
61       * Instructs the {@link InternerBuilder} to build a weak interner.
62       *
63       * @see Interners#newWeakInterner()
64       */
65      @GwtIncompatible("java.lang.ref.WeakReference")
66      public InternerBuilder weak() {
67        this.strong = false;
68        return this;
69      }
70  
71      /**
72       * Sets the concurrency level that will be used by the to-be-built {@link Interner}.
73       *
74       * @see MapMaker#concurrencyLevel(int)
75       */
76      public InternerBuilder concurrencyLevel(int concurrencyLevel) {
77        this.mapMaker.concurrencyLevel(concurrencyLevel);
78        return this;
79      }
80  
81      public <E> Interner<E> build() {
82        if (!strong) {
83          mapMaker.weakKeys();
84        }
85        return new InternerImpl<E>(mapMaker);
86      }
87    }
88  
89    /** Returns a fresh {@link InternerBuilder} instance. */
90    public static InternerBuilder newBuilder() {
91      return new InternerBuilder();
92    }
93  
94    /**
95     * Returns a new thread-safe interner which retains a strong reference to each instance it has
96     * interned, thus preventing these instances from being garbage-collected. If this retention is
97     * acceptable, this implementation may perform better than {@link #newWeakInterner}.
98     */
99    public static <E> Interner<E> newStrongInterner() {
100     return newBuilder().strong().build();
101   }
102 
103   /**
104    * Returns a new thread-safe interner which retains a weak reference to each instance it has
105    * interned, and so does not prevent these instances from being garbage-collected. This most
106    * likely does not perform as well as {@link #newStrongInterner}, but is the best alternative
107    * when the memory usage of that implementation is unacceptable.
108    */
109   @GwtIncompatible("java.lang.ref.WeakReference")
110   public static <E> Interner<E> newWeakInterner() {
111     return newBuilder().weak().build();
112   }
113 
114   @VisibleForTesting
115   static final class InternerImpl<E> implements Interner<E> {
116     // MapMaker is our friend, we know about this type
117     @VisibleForTesting
118     final MapMakerInternalMap<E, Dummy, ?, ?> map;
119 
120     private InternerImpl(MapMaker mapMaker) {
121       this.map = MapMakerInternalMap.createWithDummyValues(
122           mapMaker.keyEquivalence(Equivalence.equals()));
123     }
124 
125     @Override
126     public E intern(E sample) {
127       while (true) {
128         // trying to read the canonical...
129         InternalEntry<E, Dummy, ?> entry = map.getEntry(sample);
130         if (entry != null) {
131           E canonical = entry.getKey();
132           if (canonical != null) { // only matters if weak/soft keys are used
133             return canonical;
134           }
135         }
136 
137         // didn't see it, trying to put it instead...
138         Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE);
139         if (sneaky == null) {
140           return sample;
141         } else {
142           /* Someone beat us to it! Trying again...
143            *
144            * Technically this loop not guaranteed to terminate, so theoretically (extremely
145            * unlikely) this thread might starve, but even then, there is always going to be another
146            * thread doing progress here.
147            */
148         }
149       }
150     }
151   }
152 
153   /**
154    * Returns a function that delegates to the {@link Interner#intern} method of the given interner.
155    *
156    * @since 8.0
157    */
158   public static <E> Function<E, E> asFunction(Interner<E> interner) {
159     return new InternerFunction<E>(checkNotNull(interner));
160   }
161 
162   private static class InternerFunction<E> implements Function<E, E> {
163 
164     private final Interner<E> interner;
165 
166     public InternerFunction(Interner<E> interner) {
167       this.interner = interner;
168     }
169 
170     @Override
171     public E apply(E input) {
172       return interner.intern(input);
173     }
174 
175     @Override
176     public int hashCode() {
177       return interner.hashCode();
178     }
179 
180     @Override
181     public boolean equals(Object other) {
182       if (other instanceof InternerFunction) {
183         InternerFunction<?> that = (InternerFunction<?>) other;
184         return interner.equals(that.interner);
185       }
186 
187       return false;
188     }
189   }
190 }