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