001/* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014 015package com.google.common.collect; 016 017import static com.google.common.base.Preconditions.checkNotNull; 018 019import com.google.common.annotations.GwtIncompatible; 020import com.google.common.annotations.J2ktIncompatible; 021import com.google.common.annotations.VisibleForTesting; 022import com.google.common.base.Equivalence; 023import com.google.common.base.Function; 024import com.google.common.collect.MapMaker.Dummy; 025import com.google.common.collect.MapMakerInternalMap.InternalEntry; 026import org.jspecify.annotations.Nullable; 027 028/** 029 * Contains static methods pertaining to instances of {@link Interner}. 030 * 031 * @author Kevin Bourrillion 032 * @since 3.0 033 */ 034@J2ktIncompatible 035@GwtIncompatible 036public final class Interners { 037 private Interners() {} 038 039 /** 040 * Builder for {@link Interner} instances. 041 * 042 * @since 21.0 043 */ 044 public static class InternerBuilder { 045 private final MapMaker mapMaker = new MapMaker(); 046 private boolean strong = true; 047 048 private InternerBuilder() {} 049 050 /** 051 * Instructs the {@link InternerBuilder} to build a strong interner. 052 * 053 * @see Interners#newStrongInterner() 054 */ 055 public InternerBuilder strong() { 056 this.strong = true; 057 return this; 058 } 059 060 /** 061 * Instructs the {@link InternerBuilder} to build a weak interner. 062 * 063 * @see Interners#newWeakInterner() 064 */ 065 @GwtIncompatible("java.lang.ref.WeakReference") 066 public InternerBuilder weak() { 067 this.strong = false; 068 return this; 069 } 070 071 /** 072 * Sets the concurrency level that will be used by the to-be-built {@link Interner}. 073 * 074 * @see MapMaker#concurrencyLevel(int) 075 */ 076 public InternerBuilder concurrencyLevel(int concurrencyLevel) { 077 this.mapMaker.concurrencyLevel(concurrencyLevel); 078 return this; 079 } 080 081 public <E> Interner<E> build() { 082 if (!strong) { 083 mapMaker.weakKeys(); 084 } 085 return new InternerImpl<>(mapMaker); 086 } 087 } 088 089 /** Returns a fresh {@link InternerBuilder} instance. */ 090 public static InternerBuilder newBuilder() { 091 return new InternerBuilder(); 092 } 093 094 /** 095 * Returns a new thread-safe interner which retains a strong reference to each instance it has 096 * interned, thus preventing these instances from being garbage-collected. If this retention is 097 * acceptable, this implementation may perform better than {@link #newWeakInterner}. 098 */ 099 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 when 107 * 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 final MapMakerInternalMap<E, Dummy, ?, ?> map; 118 119 private InternerImpl(MapMaker mapMaker) { 120 this.map = 121 MapMakerInternalMap.createWithDummyValues(mapMaker.keyEquivalence(Equivalence.equals())); 122 } 123 124 @Override 125 public E intern(E sample) { 126 while (true) { 127 // trying to read the canonical... 128 @SuppressWarnings("rawtypes") // using raw types to avoid a bug in our nullness checker :( 129 InternalEntry entry = map.getEntry(sample); 130 if (entry != null) { 131 Object canonical = entry.getKey(); 132 if (canonical != null) { // only matters if weak/soft keys are used 133 // The compiler would know this is safe if not for our use of raw types (see above). 134 @SuppressWarnings("unchecked") 135 E result = (E) canonical; 136 return result; 137 } 138 } 139 140 // didn't see it, trying to put it instead... 141 Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE); 142 if (sneaky == null) { 143 return sample; 144 } else { 145 /* Someone beat us to it! Trying again... 146 * 147 * Technically this loop not guaranteed to terminate, so theoretically (extremely 148 * unlikely) this thread might starve, but even then, there is always going to be another 149 * thread doing progress here. 150 */ 151 } 152 } 153 } 154 } 155 156 /** 157 * Returns a function that delegates to the {@link Interner#intern} method of the given interner. 158 * 159 * @since 8.0 160 */ 161 public static <E> Function<E, E> asFunction(Interner<E> interner) { 162 return new InternerFunction<>(checkNotNull(interner)); 163 } 164 165 private static class InternerFunction<E> implements Function<E, E> { 166 167 private final Interner<E> interner; 168 169 public InternerFunction(Interner<E> interner) { 170 this.interner = interner; 171 } 172 173 @Override 174 public E apply(E input) { 175 return interner.intern(input); 176 } 177 178 @Override 179 public int hashCode() { 180 return interner.hashCode(); 181 } 182 183 @Override 184 public boolean equals(@Nullable Object other) { 185 if (other instanceof InternerFunction) { 186 InternerFunction<?> that = (InternerFunction<?>) other; 187 return interner.equals(that.interner); 188 } 189 190 return false; 191 } 192 } 193}