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

de.javakaffee.web.msm.LRUCache Maven / Gradle / Ivy

There is a newer version: 2.3.2
Show newest version
/*
 * Copyright 2009 Martin Grotzke
 *
 * 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 de.javakaffee.web.msm;

import java.util.AbstractList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.RandomAccess;

/**
 * An LRUCache that supports a maximum number of cache entries and a time to
 * live for them. The TTL is measured from insertion time to access time.
 *
 * @author Martin Grotzke
 * @version $Id$
 * @param 
 *            the type of the key
 * @param 
 *            the type of the value
 */
public class LRUCache {

    private final int _size;
    private final long _ttl;
    private final LinkedHashMap> _map;

    /**
     * Creates a new instance with the given maximum size.
     *
     * @param size
     *            the number of items to keep at max
     */
    public LRUCache( final int size ) {
        this( size, -1 );
    }

    /**
     * Create a new LRUCache with a maximum number of cache entries and a
     * specified time to live for cache entries. The TTL is measured from
     * insertion time to access time.
     *
     * @param size
     *            the maximum number of cached items
     * @param ttlInMillis
     *            the time to live in milli seconds. Specify -1 for no limit
     */
    public LRUCache( final int size, final long ttlInMillis ) {
        _size = size;
        _ttl = ttlInMillis;
        _map = new LinkedHashMap>( size / 2, 0.75f, true );
    }

    /**
     * Removes all cache entries.
     */
    public void clear() {
        synchronized ( _map ) {
            _map.clear();
        }
    }

    /**
     * Put the key and value.
     *
     * @param key
     *            the key
     * @param value
     *            the value
     * @return the previously associated value or null.
     */
    public V put( final K key, final V value ) {
        synchronized ( _map ) {
            final ManagedItem previous = _map.put( key, new ManagedItem( value, System.currentTimeMillis() ) );
            while ( _map.size() > _size ) {
                _map.remove( _map.keySet().iterator().next() );
            }
            return previous != null
                ? previous._value
                : null;
        }
    }

    /**
     * If the specified key is not already associated with a value or if it's
     * associated with a different value, associate it with the given value.
     * This is equivalent to
     *
     * 
     *  if (map.get(key) == null || !map.get(key).equals(value))
     *    return map.put(key, value);
     * else
     *    return map.get(key);
     * 
     * 
* * except that the action is performed atomically. * * @param key * the key to associate the value with. * @param value * the value to associate with the provided key. * @return the previous value associated with the specified key, or null if * there was no mapping for the key */ public V putIfDifferent( final K key, final V value ) { synchronized ( _map ) { final ManagedItem item = _map.get( key ); if ( item == null || item._value == null || !item._value.equals( value ) ) { return put( key, value ); } else { return item._value; } } } /** * Removes the mapping for the specified key from this map if present. * * @param key key whose mapping is to be removed from the map * @return * @return the previous value associated with key, or * null if there was no mapping for key. * (A null return can also indicate that the map * previously associated null with key.) */ public V remove( final K key ) { synchronized ( _map ) { final ManagedItem removed = _map.remove( key ); return removed != null ? removed._value : null; } } /** * Returns the value that was stored to the given key. * * @param key * the key * @return the stored value or null */ public V get( final K key ) { synchronized ( _map ) { final ManagedItem item = _map.get( key ); if ( item == null ) { return null; } if ( _ttl > -1 && System.currentTimeMillis() - item._insertionTime > _ttl ) { _map.remove( key ); return null; } return item._value; } } /** * Determines if the given key is cached without "touching" this key. * * @param key * the key * @return true if the given key is present in the underlying map, otherwise false. */ public boolean containsKey( final K key ) { synchronized ( _map ) { return _map.containsKey( key ); } } /** * The list of all keys, whose order is the order in which its entries were last accessed, * from least-recently accessed to most-recently. * * @return a new list. */ public List getKeys() { synchronized ( _map ) { return new java.util.ArrayList( _map.keySet() ); } } /** * The keys sorted by the given value comparator. * * @return the underlying set, see {@link LinkedHashMap#keySet()}. */ public List getKeysSortedByValue( final Comparator comparator ) { synchronized ( _map ) { @SuppressWarnings( "unchecked" ) final Entry>[] a = _map.entrySet().toArray( new Map.Entry[_map.size()] ); final Comparator>> c = new Comparator>>() { @Override public int compare( final Entry> o1, final Entry> o2 ) { return comparator.compare( o1.getValue()._value, o2.getValue()._value ); } }; Arrays.sort(a, c); return new LRUCache.ArrayList( a ); } } /** * Stores a value with the timestamp this value was added to the cache. * * @param * the type of the value */ private static final class ManagedItem { private final T _value; private final long _insertionTime; private ManagedItem( final T value, final long accessTime ) { _value = value; _insertionTime = accessTime; } } /** * An array list over the backed array of keys and managed items. * @author Martin Grotzke * * @param * @param */ private static class ArrayList extends AbstractList implements RandomAccess { private final Entry>[] a; ArrayList( final Entry>[] array ) { if ( array == null ) { throw new NullPointerException(); } a = array; } @Override public int size() { return a.length; } @Override public T[] toArray( final T[] a ) { throw new UnsupportedOperationException( "Not implemented." ); } @Override public E get( final int index ) { return a[index].getKey(); } @Override public E set( final int index, final E element ) { throw new UnsupportedOperationException( "Not implemented." ); } @Override public int indexOf( final Object o ) { if ( o == null ) { for ( int i = 0; i < a.length; i++ ) { if ( a[i] == null || a[i].getKey() == null ) { return i; } } } else { for ( int i = 0; i < a.length; i++ ) { if ( o.equals( a[i].getKey() ) ) { return i; } } } return -1; } @Override public boolean contains( final Object o ) { return indexOf( o ) != -1; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy