001 /*
002 * Copyright (C) 2007 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.checkNotNull;
020
021 import com.google.common.annotations.Beta;
022 import com.google.common.base.FinalizableReferenceQueue;
023 import com.google.common.base.FinalizableWeakReference;
024
025 import java.util.concurrent.ConcurrentMap;
026
027 /**
028 * Contains static methods pertaining to instances of {@link Interner}.
029 *
030 * @author Kevin Bourrillion
031 * @since 3
032 */
033 @Beta
034 public final class Interners {
035 private Interners() {}
036
037 /**
038 * Returns a new thread-safe interner which retains a strong reference to
039 * each instance it has interned, thus preventing these instances from being
040 * garbage-collected. If this retention is acceptable, this implementation may
041 * perform better than {@link #newWeakInterner}.
042 */
043 public static <E> Interner<E> newStrongInterner() {
044 final ConcurrentMap<E, E> map = new MapMaker().makeMap();
045 return new Interner<E>() {
046 public E intern(E sample) {
047 E canonical = map.putIfAbsent(checkNotNull(sample), sample);
048 return (canonical == null) ? sample : canonical;
049 }
050 };
051 }
052
053 /**
054 * Returns a new thread-safe interner which retains a weak reference to each
055 * instance it has interned, and so does not prevent these instances from
056 * being garbage-collected. This most likely does not perform as well as
057 * {@link #newStrongInterner}, but is the best alternative when the memory
058 * usage of that implementation is unacceptable.
059 */
060 public static <E> Interner<E> newWeakInterner() {
061 return new WeakInterner<E>();
062 }
063
064 private static class WeakInterner<E> implements Interner<E> {
065 private final ConcurrentMap<InternReference, InternReference> map
066 = new MapMaker().makeMap();
067
068 public E intern(final E sample) {
069 final int hashCode = sample.hashCode();
070
071 // TODO: once MapMaker supports arbitrary Equivalence, we won't need the
072 // dummy instance anymore
073 Object fakeReference = new Object() {
074 @Override public int hashCode() {
075 return hashCode;
076 }
077 @Override public boolean equals(Object object) {
078 if (object.hashCode() != hashCode) {
079 return false;
080 }
081 /*
082 * Implicitly an unchecked cast to WeakInterner<?>.InternReference,
083 * though until OpenJDK 7, the compiler doesn't recognize this. If we
084 * could explicitly cast to the wildcard type
085 * WeakInterner<?>.InternReference, that would be sufficient for our
086 * purposes. The compiler, however, rejects such casts (or rather, it
087 * does until OpenJDK 7).
088 *
089 * See Sun bug 6665356.
090 */
091 @SuppressWarnings("unchecked")
092 InternReference that = (InternReference) object;
093 return sample.equals(that.get());
094 }
095 };
096
097 // Fast-path; avoid creating the reference if possible
098 InternReference existingRef = map.get(fakeReference);
099 if (existingRef != null) {
100 E canonical = existingRef.get();
101 if (canonical != null) {
102 return canonical;
103 }
104 }
105
106 InternReference newRef = new InternReference(sample, hashCode);
107 while (true) {
108 InternReference sneakyRef = map.putIfAbsent(newRef, newRef);
109 if (sneakyRef == null) {
110 return sample;
111 } else {
112 E canonical = sneakyRef.get();
113 if (canonical != null) {
114 return canonical;
115 }
116 }
117 }
118 }
119
120 private static final FinalizableReferenceQueue frq
121 = new FinalizableReferenceQueue();
122
123 class InternReference extends FinalizableWeakReference<E> {
124 final int hashCode;
125
126 InternReference(E key, int hash) {
127 super(key, frq);
128 hashCode = hash;
129 }
130 public void finalizeReferent() {
131 map.remove(this);
132 }
133 @Override public E get() {
134 E referent = super.get();
135 if (referent == null) {
136 finalizeReferent();
137 }
138 return referent;
139 }
140 @Override public int hashCode() {
141 return hashCode;
142 }
143 @Override public boolean equals(Object object) {
144 // TODO: should we try to do something to make equals() somewhat more
145 // normal?
146 if (object == this) {
147 return true;
148 }
149 if (object instanceof WeakInterner.InternReference) {
150 /*
151 * On the following line, Eclipse wants a type parameter, producing
152 * WeakInterner<?>.InternReference. The problem is that javac rejects
153 * that form. Omitting WeakInterner satisfies both, though this seems
154 * odd, since we are inside a WeakInterner<E> and thus the
155 * WeakInterner<E> is implied, yet there is no reason to believe that
156 * the other object's WeakInterner has type E. That's right -- we've
157 * found a way to perform an unchecked cast without receiving a
158 * warning from either Eclipse or javac. Taking advantage of that
159 * seems questionable, even though we don't depend upon the type of
160 * that.get(), so we'll just suppress the warning.
161 */
162 @SuppressWarnings("unchecked")
163 WeakInterner.InternReference that =
164 (WeakInterner.InternReference) object;
165 if (that.hashCode != hashCode) {
166 return false;
167 }
168 E referent = super.get();
169 return referent != null && referent.equals(that.get());
170 }
171 return object.equals(this);
172 }
173 }
174 }
175 }