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 }