/*
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Equivalences;
import com.google.common.base.Function;
import com.google.common.collect.CustomConcurrentHashMap.ReferenceEntry;
import java.util.concurrent.ConcurrentMap;
/**
* Contains static methods pertaining to instances of {@link Interner}.
*
* @author Kevin Bourrillion
* @since 3.0
*/
@Beta
public final class Interners {
private Interners() {}
/**
* Returns a new thread-safe interner which retains a strong reference to each instance it has
* interned, thus preventing these instances from being garbage-collected. If this retention is
* acceptable, this implementation may perform better than {@link #newWeakInterner}. Note that
* unlike {@link String#intern}, using this interner does not consume memory in the permanent
* generation.
*/
public static <E> Interner<E> newStrongInterner() {
final ConcurrentMap<E, E> map = new MapMaker().makeMap();
return new Interner<E>() {
@Override public E intern(E sample) {
E canonical = map.putIfAbsent(checkNotNull(sample), sample);
return (canonical == null) ? sample : canonical;
}
};
}
private static class CustomInterner<E> implements Interner<E> {
// MapMaker is our friend, we know about this type
private final CustomConcurrentHashMap<E, Dummy> map;
CustomInterner(GenericMapMaker<? super E, Object> mm) {
this.map = mm
.strongValues()
.keyEquivalence(Equivalences.equals())
.makeCustomMap();
}
@Override public E intern(E sample) {
while (true) {
// trying to read the canonical...
ReferenceEntry<E, Dummy> entry = map.getEntry(sample);
if (entry != null) {
E canonical = entry.getKey();
if (canonical != null) { // only matters if weak/soft keys are used
return canonical;
}
}
// didn't see it, trying to put it instead...
Dummy sneaky = map.putIfAbsent(sample, Dummy.VALUE);
if (sneaky == null) {
return sample;
} else {
/* Someone beat us to it! Trying again...
*
* Technically this loop not guaranteed to terminate, so theoretically (extremely
* unlikely) this thread might starve, but even then, there is always going to be another
* thread doing progress here.
*/
}
}
}
private enum Dummy { VALUE }
}
/**
* Returns a new thread-safe interner which retains a weak reference to each instance it has
* interned, and so does not prevent these instances from being garbage-collected. This most
* likely does not perform as well as {@link #newStrongInterner}, but is the best alternative
* when the memory usage of that implementation is unacceptable. Note that unlike {@link
* String#intern}, using this interner does not consume memory in the permanent generation.
*/
@GwtIncompatible("java.lang.ref.WeakReference")
public static <E> Interner<E> newWeakInterner() {
return new CustomInterner<E>(new MapMaker().weakKeys());
}
/**
* Returns a function that delegates to the {@link Interner#intern} method of the given interner.
*
* @since 8.0
*/
public static <E> Function<E, E> asFunction(Interner<E> interner) {
return new InternerFunction<E>(checkNotNull(interner));
}
private static class InternerFunction<E> implements Function<E, E> {
private final Interner<E> interner;
public InternerFunction(Interner<E> interner) {
this.interner = interner;
}
@Override public E apply(E input) {
return interner.intern(input);
}
@Override public int hashCode() {
return interner.hashCode();
}
@Override public boolean equals(Object other) {
if (other instanceof InternerFunction<?>) {
InternerFunction<?> that = (InternerFunction<?>) other;
return interner.equals(that.interner);
}
return false;
}
}
}
|