All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.strobel.collections.Cache Maven / Gradle / Ivy

/*
 * Cache.java
 *
 * Copyright (c) 2012 Mike Strobel
 *
 * This source code is subject to terms and conditions of the Apache License, Version 2.0.
 * A copy of the license can be found in the License.html file at the root of this distribution.
 * By using this source code in any fashion, you are agreeing to be bound by the terms of the
 * Apache License, Version 2.0.
 *
 * You must not remove this notice, or any other, from this software.
 */

package com.strobel.collections;

import com.strobel.annotations.Nullable;
import com.strobel.core.VerifyArgument;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author strobelm
 */
public abstract class Cache {
    protected Cache() {
    }

    /**
     * Gets a value indicating whether a cached value exists for the given key.
     */
    public boolean contains(final K key) {
        return get(key) != null;
    }

    /**
     * Gets a value indicating whether the cached value matches the given value
     * for a specified key.
     */
    public boolean contains(final K key, final V value) {
        final V cachedValue = get(key);
        return cachedValue != null && cachedValue.equals(value);
    }

    /**
     * Returns a thread-specific satellite cache chained to this cache.  If the target
     * cache is already a satellite cache, it will simply return itself.  Note that the
     * returned cache is completely unsynchronized and is not safe for concurrent access.
     *
     * @return A thread-specific satellite cache.
     */
    public abstract Cache getSatelliteCache();

    /**
     * 

* Replaces the value associated with a given key if the current value matches the * expected value. *

*

* Note that the replaced value will not be propagated to child caches that * already have a value for the same key. As such, this method is of limited usefulness * and should only be called on isolated Level 1 caches. *

* * @param key * The key for which to change the associated value. * @param expectedValue * The expected value to be replaced. * @param updatedValue * The new value. * * @return {@code true} if the expected value was replaced; otherwise, {@code false}. */ public abstract boolean replace(final K key, @Nullable final V expectedValue, final V updatedValue); /** * Gets the value associated with the given key. * * @param key * The key associated with the desired value. * * @return The value corresponding the given key, or {@code null} if no value was found. */ public abstract V get(final K key); /** * Places a value in the cache only if no value exists with the same key. * * @param key * The key associated with the given value. * @param value * The value to insert into the cache. * * @return The cached value associated with the given key, which will be the provided * value if no existing value was found. */ public abstract V cache(final K key, final V value); /** * Creates a concurrency-safe Level 1 cache that may be used in isolation or as * the root cache in a multi-level cache design. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createTopLevelCache() { return new TopLevelCache<>(); } /** * Creates an unsynchronized, concurrency-unsafe Level 1 cache that can only be * used safely by a single thread. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createSatelliteCache() { return new SatelliteCache<>(); } /** * Creates an unsynchronized, concurrency-unsafe Level 2 cache that can only be * used safely by a single thread. On a cache miss, the parent cache will be * checked. On an insert, the value will propagate up to the parent cache. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createSatelliteCache(final Cache parent) { return new SatelliteCache<>(VerifyArgument.notNull(parent, "parent")); } /** * Creates an unsynchronized, concurrency-unsafe Level 1 cache that can only be * used safely by a single thread. Keys are compared by reference identity. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createSatelliteIdentityCache() { return new SatelliteCache<>(); } /** * Creates an unsynchronized, concurrency-unsafe Level 2 cache that can only be * used safely by a single thread. On a cache miss, the parent cache will be * checked. On an insert, the value will propagate up to the parent cache. * Keys are compared by reference identity. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createSatelliteIdentityCache(final Cache parent) { return new SatelliteCache<>(VerifyArgument.notNull(parent, "parent")); } /** * Creates a Level 1 cache that internally maintains a separate satellite cache * for each thread that accesses it. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createThreadLocalCache() { return new ThreadLocalCache<>(); } /** * Creates a Level 1 cache that internally maintains a separate satellite cache * for each thread that accesses it. Keys are compared by reference identity. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createThreadLocalIdentityCache() { return new ThreadLocalCache<>(); } /** * Creates a Level 2 cache that internally maintains a separate satellite cache * for each thread that accesses it. On a cache miss, the parent cache will be * checked. On an insert, the value will propagate up to the parent cache. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createThreadLocalCache(final Cache parent) { return new ThreadLocalCache<>(VerifyArgument.notNull(parent, "parent")); } /** * Creates a Level 2 cache that internally maintains a separate satellite cache * for each thread that accesses it. On a cache miss, the parent cache will be * checked. On an insert, the value will propagate up to the parent cache. * Keys are compared by reference identity. * * @param * The type of keys used to identify values in the cache. * @param * The type of values stored in the cache. * * @return The newly created cache. */ public static Cache createThreadLocalIdentityCache(final Cache parent) { return new ThreadLocalIdentityCache<>(VerifyArgument.notNull(parent, "parent")); } } final class TopLevelCache extends Cache { private final ConcurrentHashMap _cache = new ConcurrentHashMap<>(); @Override public V cache(final K key, final V value) { final V cachedValue = _cache.putIfAbsent(key, value); return cachedValue != null ? cachedValue : value; } @Override public Cache getSatelliteCache() { return createSatelliteCache(this); } @Override public boolean replace(final K key, final V expectedValue, final V updatedValue) { if (expectedValue == null) { return _cache.putIfAbsent(key, updatedValue) == null; } return _cache.replace(key, expectedValue, updatedValue); } @Override public V get(final K key) { return _cache.get(key); } } final class SatelliteCache extends Cache { private final Cache _parent; private final HashMap _cache = new HashMap<>(); public SatelliteCache() { _parent = null; } @Override public Cache getSatelliteCache() { return this; } @Override public boolean replace(final K key, final V expectedValue, final V updatedValue) { if (_parent != null && !_parent.replace(key, expectedValue, updatedValue)) { return false; } _cache.put(key, updatedValue); return true; } public SatelliteCache(final Cache parent) { _parent = parent; } @Override public V cache(final K key, final V value) { V cachedValue = _cache.get(key); if (cachedValue != null) { return cachedValue; } if (_parent != null) { cachedValue = _parent.cache(key, value); } else { cachedValue = value; } _cache.put(key, cachedValue); return cachedValue; } @Override public V get(final K key) { V cachedValue = _cache.get(key); if (cachedValue != null) { return cachedValue; } if (_parent != null) { cachedValue = _parent.get(key); if (cachedValue != null) { _cache.put(key, cachedValue); } } return cachedValue; } } final class ThreadLocalCache extends Cache { private final Cache _parent; @SuppressWarnings("ThreadLocalNotStaticFinal") private final ThreadLocal> _threadCaches = new ThreadLocal>() { @Override protected SatelliteCache initialValue() { return new SatelliteCache<>(_parent); } }; public ThreadLocalCache() { _parent = null; } @Override public Cache getSatelliteCache() { return _threadCaches.get(); } @Override public boolean replace(final K key, final V expectedValue, final V updatedValue) { return _threadCaches.get().replace(key, expectedValue, updatedValue); } public ThreadLocalCache(final Cache parent) { _parent = parent; } @Override public V cache(final K key, final V value) { return _threadCaches.get().cache(key, value); } @Override public V get(final K key) { return _threadCaches.get().get(key); } } final class ThreadLocalIdentityCache extends Cache { private final Cache _parent; @SuppressWarnings("ThreadLocalNotStaticFinal") private final ThreadLocal> _threadCaches = new ThreadLocal>() { @Override protected SatelliteCache initialValue() { return new SatelliteCache<>(_parent); } }; public ThreadLocalIdentityCache() { _parent = null; } @Override public Cache getSatelliteCache() { return _threadCaches.get(); } @Override public boolean replace(final K key, final V expectedValue, final V updatedValue) { return _threadCaches.get().replace(key, expectedValue, updatedValue); } public ThreadLocalIdentityCache(final Cache parent) { _parent = parent; } @Override public V cache(final K key, final V value) { return _threadCaches.get().cache(key, value); } @Override public V get(final K key) { return _threadCaches.get().get(key); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy