org.codelibs.elasticsearch.taste.common.Cache Maven / Gradle / Ivy
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.codelibs.elasticsearch.taste.common;
import java.util.Iterator;
import com.google.common.base.Preconditions;
/**
*
* An efficient Map-like class which caches values for keys. Values are not "put" into a {@link Cache};
* instead the caller supplies the instance with an implementation of {@link Retriever} which can load the
* value for a given key.
*
*
*
* The cache does not support {@code null} keys.
*
*
*
* Thanks to Amila Jayasooriya for helping evaluate performance of the rewrite of this class, as part of a
* Google Summer of Code 2007 project.
*
*/
public final class Cache implements Retriever {
private static final Object NULL = new Object();
private final FastMap cache;
private final Retriever super K, ? extends V> retriever;
/**
*
* Creates a new cache based on the given {@link Retriever}.
*
*
* @param retriever
* object which can retrieve values for keys
*/
public Cache(final Retriever super K, ? extends V> retriever) {
this(retriever, FastMap.NO_MAX_SIZE);
}
/**
*
* Creates a new cache based on the given {@link Retriever} and with given maximum size.
*
*
* @param retriever
* object which can retrieve values for keys
* @param maxEntries
* maximum number of entries the cache will store before evicting some
*/
public Cache(final Retriever super K, ? extends V> retriever,
final int maxEntries) {
Preconditions.checkArgument(retriever != null, "retriever is null");
Preconditions.checkArgument(maxEntries >= 1,
"maxEntries must be at least 1");
cache = new FastMap(11, maxEntries);
this.retriever = retriever;
}
/**
*
* Returns cached value for a key. If it does not exist, it is loaded using a {@link Retriever}.
*
*
* @param key
* cache key
* @return value for that key
*/
@Override
public V get(final K key) {
V value;
synchronized (cache) {
value = cache.get(key);
}
if (value == null) {
return getAndCacheValue(key);
}
return value == NULL ? null : value;
}
/**
*
* Uncaches any existing value for a given key.
*
*
* @param key
* cache key
*/
public void remove(final K key) {
synchronized (cache) {
cache.remove(key);
}
}
/**
* Clears all cache entries whose key matches the given predicate.
*/
public void removeKeysMatching(final MatchPredicate predicate) {
synchronized (cache) {
final Iterator it = cache.keySet().iterator();
while (it.hasNext()) {
final K key = it.next();
if (predicate.matches(key)) {
it.remove();
}
}
}
}
/**
* Clears all cache entries whose value matches the given predicate.
*/
public void removeValueMatching(final MatchPredicate predicate) {
synchronized (cache) {
final Iterator it = cache.values().iterator();
while (it.hasNext()) {
final V value = it.next();
if (predicate.matches(value)) {
it.remove();
}
}
}
}
/**
*
* Clears the cache.
*
*/
public void clear() {
synchronized (cache) {
cache.clear();
}
}
private V getAndCacheValue(final K key) {
V value = retriever.get(key);
if (value == null) {
value = (V) NULL;
}
synchronized (cache) {
cache.put(key, value);
}
return value;
}
@Override
public String toString() {
return "Cache[retriever:" + retriever + ']';
}
/**
* Used by {#link #removeKeysMatching(Object)} to decide things that are matching.
*/
public interface MatchPredicate {
boolean matches(T thing);
}
}