001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.cache.implementation;
011:
012: import org.mmbase.cache.CacheImplementationInterface;
013: import java.util.*;
014:
015: /**
016: * A cache implementation backed by a {@link java.util.LinkedHashMap}, in access-order mode, and
017: * restricted maximal size ('Least Recently Used' cache algorithm).
018: *
019: * @author Michiel Meeuwissen
020: * @version $Id: LRUCache.java,v 1.2 2008/02/03 17:33:57 nklasens Exp $
021: * @see org.mmbase.cache.Cache
022: * @since MMBase-1.9
023: */
024: public class LRUCache<K, V> implements
025: CacheImplementationInterface<K, V> {
026:
027: public int maxSize = 100;
028: private final Map<K, V> backing;
029:
030: public LRUCache() {
031: this (100);
032: }
033:
034: public LRUCache(int size) {
035: maxSize = size;
036: // caches can typically be accessed/modified by multipible thread, so we need to synchronize
037: backing = Collections.synchronizedMap(new LinkedHashMap<K, V>(
038: size, 0.75f, true) {
039: protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
040: return size() > LRUCache.this .maxSize;
041: }
042: });
043: }
044:
045: public int getCount(K key) {
046: return -1;
047: }
048:
049: /**
050: * Change the maximum size of the table.
051: * This may result in removal of entries in the table.
052: * @param size the new desired size
053: */
054: public void setMaxSize(int size) {
055: if (size < 1)
056: throw new IllegalArgumentException(
057: "Cannot set size of to non-positive value");
058: maxSize = size;
059: while (size() > maxSize()) {
060: try {
061: Iterator<Entry<K, V>> i = entrySet().iterator();
062: i.next();
063: i.remove();
064: } catch (Exception e) {
065: // ConcurentModification?
066: }
067: }
068: }
069:
070: public int maxSize() {
071: return maxSize;
072: }
073:
074: /**
075: * Returns size, maxSize.
076: */
077: public String toString() {
078: return "Size=" + size() + ", Max=" + maxSize;
079: }
080:
081: public void config(Map<String, String> map) {
082: // needs no configuration.
083: }
084:
085: // wrapping for synchronization
086: public int size() {
087: return backing.size();
088: }
089:
090: public boolean isEmpty() {
091: return backing.isEmpty();
092: }
093:
094: public boolean containsKey(Object key) {
095: return backing.containsKey(key);
096: }
097:
098: public boolean containsValue(Object value) {
099: return backing.containsValue(value);
100: }
101:
102: public V get(Object key) {
103: return backing.get(key);
104: }
105:
106: public V put(K key, V value) {
107: return backing.put(key, value);
108: }
109:
110: public V remove(Object key) {
111: return backing.remove(key);
112: }
113:
114: public void putAll(Map<? extends K, ? extends V> map) {
115: backing.putAll(map);
116: }
117:
118: public void clear() {
119: backing.clear();
120: }
121:
122: public Set<K> keySet() {
123: return backing.keySet();
124: }
125:
126: public Set<Map.Entry<K, V>> entrySet() {
127: return backing.entrySet();
128: }
129:
130: public Collection<V> values() {
131: return backing.values();
132: }
133:
134: }
|