com.cinchapi.concourse.cache.ReferenceCache.java Source code

Java tutorial

Introduction

Here is the source code for com.cinchapi.concourse.cache.ReferenceCache.java

Source

/*
 * Copyright (c) 2013-2016 Cinchapi Inc.
 *
 * 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.cinchapi.concourse.cache;

import javax.annotation.Nullable;

import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;

/**
 * <p>
 * A concurrent cache that holds references to objects so as to prevent
 * unnecessary constructor calls. The cache does not have a maximum capacity or
 * expulsion policy, but it uses a SoftReference for each stored object so that
 * the garbage collector will clear the cache only before an OOM occurs.
 * </p>
 * <h2>Example:</h2>
 * 
 * <code><pre>
 *  private class Foo {
 *    
 *    int id;
 *    String label;
 *    Bar bar;
 *    
 *    private static final ReferenceCache{@literal <Foo>} cache = new ReferenceCache{@literal <Foo>}();
 *    
 *    public static Foo newInstance(int id, String label, Bar bar){
 *       Foo foo = cache.get(id, label, bar); // use the combination of id, label and bar as a cacheKey
 *       if(foo == null){
 *           foo = new Foo(id, label, bar);
 *          cache.put(foo, id, label, bar);
 *       }
 *       return foo;
 *    }
 *    
 *    private Foo(int id, String label, Bar bar){
 *       this.id = id;
 *       this.label = label;
 *       this.bar = bar;
 *    }
 * }
 * </pre></code>
 * 
 * @author Jeff Nelson
 * @param <T> - the cached object type.
 */
public class ReferenceCache<T> {

    private static final int INITIAL_CAPACITY = 500000;
    private static final int CONCURRENCY_LEVEL = 16;

    private final Cache<HashCode, T> cache = CacheBuilder.newBuilder().initialCapacity(INITIAL_CAPACITY)
            .concurrencyLevel(CONCURRENCY_LEVEL).softValues().build();

    /**
     * Return the cache value associated with the group of {@code args} or
     * {@code null} if not value is found.
     * 
     * @param args
     * @return the cached value.
     */
    @Nullable
    public T get(Object... args) {
        HashCode id = getCacheKey(args);
        return cache.getIfPresent(id);
    }

    /**
     * Cache {@code value} and associated it with the group of {@code args}.
     * Each arg should be a value that is used to construct
     * the object.
     * 
     * @param value
     * @param args
     */
    public void put(T value, Object... args) {
        Preconditions.checkNotNull(value);
        Preconditions.checkNotNull(args);
        Preconditions.checkArgument(args.length > 0, "You must specify at least one key");
        HashCode id = getCacheKey(args);
        cache.put(id, value);
    }

    /**
     * Remove the value associated with the group of {@code args} from the
     * cache.
     * 
     * @param args
     */
    public void remove(Object... args) {
        cache.invalidate(getCacheKey(args));
    }

    /**
     * Return a unique identifier for a group of {@code args}.
     * 
     * @param args
     * @return the identifier.
     */
    private HashCode getCacheKey(Object... args) {
        StringBuilder key = new StringBuilder();
        for (Object o : args) {
            key.append(o.hashCode());
            key.append(o.getClass().getName());
        }
        return Hashing.md5().hashUnencodedChars(key.toString());
    }

}