lrucache.java

来自「RESIN 3.2 最新源码」· Java 代码 · 共 798 行 · 第 1/2 页

JAVA
798
字号
/* * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT.  See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the *   Free Software Foundation, Inc. *   59 Temple Place, Suite 330 *   Boston, MA 02111-1307  USA * * @author Scott Ferguson */package com.caucho.util;import java.util.ArrayList;import java.util.Iterator;/** * Fixed length cache with a LRU replacement policy.  If cache items * implement CacheListener, they will be informed when they're removed * from the cache. * * <p>Null keys are not allowed.  LruCache is synchronized. */public class LruCache<K,V> {  private static final Integer NULL = new Integer(0);    // hash table containing the entries.  Its size is twice the capacity  // so it will always remain at least half empty  private CacheItem []_entries;  private boolean _isEnableListeners = true;    // maximum allowed entries  private int _capacity;  // size 1 capacity is half the actual capacity  private int _capacity1;    // mask for hash mapping  private int _mask;    // number of items in the cache seen once  private int _size1;  // head of the LRU list  private CacheItem<K,V> _head1;  // tail of the LRU list  private CacheItem<K,V> _tail1;    // number of items in the cache seen more than once  private int _size2;  // head of the LRU list  private CacheItem<K,V> _head2;  // tail of the LRU list  private CacheItem<K,V> _tail2;  // hit count statistics  private volatile long _hitCount;  // miss count statistics  private volatile long _missCount;    /**   * Create the LRU cache with a specific capacity.   *   * @param initialCapacity minimum capacity of the cache   */  public LruCache(int initialCapacity)  {    int capacity;    for (capacity = 16; capacity < 2 * initialCapacity; capacity *= 2) {    }    _entries = new CacheItem[capacity];    _mask = capacity - 1;    _capacity = initialCapacity;    _capacity1 = _capacity / 2;  }  /**   * Disable the listeners   */  public void setEnableListeners(boolean isEnable)  {    _isEnableListeners = isEnable;  }  /**   * Returns the current number of entries in the cache.   */  public int size()  {    return _size1 + _size2;  }  /**   * Returns the LRU cache capacity   */  public int getCapacity()  {    return _capacity;  }  /**   * Clears the cache   */  public void clear()  {    if (_size1 == 0 && _size2 == 0)      return;        ArrayList<CacheListener> listeners = null;    synchronized (this) {      for (int i = _entries.length - 1; i >= 0; i--) {        CacheItem<K,V> item = _entries[i];        _entries[i] = null;	if (_isEnableListeners) {	  for (; item != null; item = item._nextHash) {	    if (item._value instanceof CacheListener) {	      if (listeners == null)		listeners = new ArrayList<CacheListener>();	      listeners.add((CacheListener) item._value);	    }	  }        }      }      _size1 = 0;      _head1 = null;      _tail1 = null;      _size2 = 0;      _head2 = null;      _tail2 = null;    }    for (int i = listeners != null ? listeners.size() - 1 : -1;	 i >= 0;	 i--) {      CacheListener listener = listeners.get(i);      listener.removeEvent();    }  }  /**   * Get an item from the cache and make it most recently used.   *   * @param key key to lookup the item   * @return the matching object in the cache   */  public V get(K key)  {    Object okey = key;    if (okey == null)      okey = NULL;        int hash = okey.hashCode() & _mask;    synchronized (this) {      for (CacheItem<K,V> item = _entries[hash];	   item != null;	   item = item._nextHash) {	if (item._key == okey || item._key.equals(okey)) {	  updateLru(item);	  _hitCount++;	  return item._value;	}      }      _missCount++;    }    return null;  }  /**   * Puts a new item in the cache.  If the cache is full, remove the   * LRU item.   *   * @param key key to store data   * @param value value to be stored   *   * @return old value stored under the key   */  public V put(K key, V value)  {    V oldValue = put(key, value, true);    if (_isEnableListeners && oldValue instanceof CacheListener)      ((CacheListener) oldValue).removeEvent();    return oldValue;  }  /**   * Puts a new item in the cache.  If the cache is full, remove the   * LRU item.   *   * @param key key to store data   * @param value value to be stored   *   * @return the value actually stored   */  public V putIfNew(K key, V value)  {    V oldValue = put(key, value, false);    if (oldValue != null)      return oldValue;    else      return value;  }  /**   * Puts a new item in the cache.  If the cache is full, remove the   * LRU item.   *   * @param key key to store data   * @param value value to be stored   *   * @return old value stored under the key   */  private V put(K key, V value, boolean replace)  {    Object okey = key;        if (okey == null)      okey = NULL;    // remove LRU items until we're below capacity    while (_capacity <= _size1 + _size2) {      if (! removeTail())	throw new IllegalStateException("unable to remove tail from cache");    }    int hash = okey.hashCode() & _mask;    V oldValue = null;    synchronized (this) {      CacheItem<K,V> item = _entries[hash];            for (;	   item != null;	   item = item._nextHash) {	// matching item gets replaced	if (item._key == okey || okey.equals(item._key)) {	  updateLru(item);	  oldValue = item._value;	  if (replace)	    item._value = value;	  	  if (value == oldValue)	    oldValue = null;	  break;	}      }      if (item == null) {	CacheItem<K,V> next = _entries[hash];		item = new CacheItem<K,V>((K) okey, value);		item._nextHash = next;	if (next != null)	  next._prevHash = item;		_entries[hash] = item;	_size1++;	  	item._nextLru = _head1;	if (_head1 != null)	  _head1._prevLru = item;	else	  _tail1 = item;	_head1 = item;	return null;      }      if (_isEnableListeners && replace	  && oldValue instanceof SyncCacheListener)	((SyncCacheListener) oldValue).syncRemoveEvent();    }    if (_isEnableListeners && replace && oldValue instanceof CacheListener)      ((CacheListener) oldValue).removeEvent();    return oldValue;  }  /**   * Put item at the head of the used-twice lru list.   * This is always called while synchronized.   */  private void updateLru(CacheItem<K,V> item)  {    CacheItem<K,V> prevLru = item._prevLru;    CacheItem<K,V> nextLru = item._nextLru;    if (item._hitCount++ == 1) {      if (prevLru != null)	prevLru._nextLru = nextLru;      else	_head1 = nextLru;      if (nextLru != null)	nextLru._prevLru = prevLru;      else	_tail1 = prevLru;      item._prevLru = null;      if (_head2 != null)	_head2._prevLru = item;      else	_tail2 = item;            item._nextLru = _head2;      _head2 = item;      _size1--;      _size2++;    }    else {      if (prevLru == null)	return;            prevLru._nextLru = nextLru;      item._prevLru = null;      item._nextLru = _head2;            _head2._prevLru = item;      _head2 = item;            if (nextLru != null)	nextLru._prevLru = prevLru;      else	_tail2 = prevLru;    }  }  /**   * Remove the last item in the LRU   */  public boolean removeTail()  {    CacheItem<K,V> tail;    synchronized (this) {      if (_capacity1 <= _size1)	tail = _tail1;      else if (_size2 > 0)	tail = _tail2;      else if (_size1 > 0)	tail = _tail1;      else	return false;    }        remove(tail._key);        return true;  }  /**   * Remove the last item in the LRU.  In this case, remove from the   * list with the longest length.   *   * For functions like Cache disk space, this is a better solution   * than the struct LRU removal.   */  public boolean removeLongestTail()  {    CacheItem<K,V> tail;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?