/* * Copyright 1999-2004 The Apache Software Foundation * * 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 net.jxta.impl.util; import java.util.HashMap; import java.util.Map; /** * This class implements a Generic LRU Cache. * * @author Ignacio J. Ortega * @author Mohamed Abdelaziz */ public class LRUCache<K, V> { private final transient int cacheSize; private transient int currentSize; private transient CacheNode<K, V> first; private transient CacheNode<K, V> last; private final transient Map<K, CacheNode<K, V>> nodes; /** * Constructor for the LRUCache object * * @param size Description of the Parameter */ public LRUCache(int size) { currentSize = 0; cacheSize = size; nodes = new HashMap<K, CacheNode<K, V>>(size); } /** * clear the cache */ public synchronized void clear() { first = null; last = null; nodes.clear(); currentSize = 0; } /** * returns the number of elements currently in cache * * @return the number of elements in cache */ public synchronized int size() { return currentSize; } /** * retrieve an object from cache * * @param key key * @return object */ public synchronized V get(K key) { CacheNode<K, V> node = nodes.get(key); if (node != null) { moveToHead(node); return node.value; } return null; } public synchronized boolean contains(K key) { return nodes.keySet().contains(key); } private void moveToHead(CacheNode<K, V> node) { if (node == first) { return; } if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } if (last == node) { last = node.prev; } if (first != null) { node.next = first; first.prev = node; } first = node; node.prev = null; if (last == null) { last = first; } } /** * puts an object into cache * * @param key key to store value by * @param value object to insert */ public synchronized void put(K key, V value) { CacheNode<K, V> node = nodes.get(key); if (node == null) { if (currentSize >= cacheSize) { if (last != null) { nodes.remove(last.key); } removeLast(); } else { currentSize++; } node = new CacheNode<K, V>(key, value); } node.value = value; moveToHead(node); nodes.put(key, node); } /** * remove an object from cache * * @param key key * @return Object removed */ public synchronized V remove(K key) { CacheNode<K, V> node = nodes.get(key); if (node != null) { if (node.prev != null) { node.prev.next = node.next; } if (node.next != null) { node.next.prev = node.prev; } if (last == node) { last = node.prev; } if (first == node) { first = node.next; } } if (node != null) { return node.value; } else { return null; } } /** * removes the last entry from cache */ private void removeLast() { if (last != null) { if (last.prev != null) { last.prev.next = null; } else { first = null; } last = last.prev; } } /** * cache node object wrapper */ protected class CacheNode<K, V> { final K key; CacheNode<K, V> next; CacheNode<K, V> prev; V value; /** * Constructor for the CacheNode object * * @param key key * @param value value */ CacheNode(K key, V value) { this.key = key; this.value = value; } } }