⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cachemap.java

📁 非常接近C/S操作方式的Java Ajax框架-ZK 用ZK框架使你的B/S应用程序更漂亮更易操作。 官网:www.zkoss.org
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/* CacheMap.java{{IS_NOTE	Purpose: 	Description: 	History:	2001/11/23 15:26:21, Create, Tom M. Yeh.}}IS_NOTECopyright (C) 2001 Potix Corporation. All Rights Reserved.{{IS_RIGHT	This program is distributed under GPL Version 2.0 in the hope that	it will be useful, but WITHOUT ANY WARRANTY.}}IS_RIGHT*/package org.zkoss.util;import java.util.AbstractCollection;import java.util.Collection;import java.util.Map;import java.util.AbstractSet;import java.util.Set;import java.util.LinkedHashMap;import java.util.Iterator;import java.lang.ref.Reference;import java.lang.ref.WeakReference;import java.lang.ref.ReferenceQueue;import org.zkoss.lang.D;import org.zkoss.lang.Objects;import org.zkoss.util.logging.Log;/** * The cache map. The key-to-value mappings hold in this map is * temporary. They are removed when GC demanding memory and a * criteria is met. The criteria is whether the mapping is old enough * (called lifetime), or the upper bound is hit (called max-size). * * <p>The criteria can be changed by overriding {@link #canExpunge}. * When to check the criteria can be changed by overriding * {@link #shallExpunge}. * * <p>If the criteria is totally independent of GC, you could override * {@link #newQueue} to return null. Then, {@link #shallExpunge} * always returns true (rather than when GC is activated) -- of course, * you could override {@link #shallExpunge}, too. * * <p>The constructor doesn't provide parameter to set the lifetime * or maxsize. However, {@link #setLifetime} and {@link #setMaxSize} * return the map, so you can do:<br> * <code>Map map = new CacheMap().setLifetime(10000);</code> * * <p>It is very different from WeakHashMap: * * <ul> *  <li>The mapping might be removed even if the key is hold somewhere *  (i.e., strong reachable).</li> *  <li>The mapping might not be removed when GC demanding memory *  if the criteria doesn't meet.</li> *  <li>It is not serializable.</li> * </ul> * * <p>Like other maps, it is not thread-safe. To get one, use * java.util.Collections.synchronizedMap. * * <p>Implementation Note: there is another version of CacheMap that * uses WeakReference for each value (refer to obsolete). * The drawback is that all mapping will be queued and need to be examined, * because GC tends to release all reference at once. * * <p>We don't use PhantomReference because it is still required to * re-create the reference after enqueued. * * @author tomyeh */public class CacheMap implements Map, java.io.Serializable, Cloneable {    private static final long serialVersionUID = 20060622L;	//private static final Log log = Log.lookup(CacheMap.class);	/** The default minimal lifetime, unit=milliseconds. It is 30 minutes. */	public static final int DEFAULT_LIFETIME = 30 * 60 * 1000;	/** The default maximal allowed size. It is 1024. */	public static final int DEFAULT_MAXSIZE = 1024;	/** The map to store the mappings. */	private Map _map; //it is OK to serialized	/** The minimal lifetime. */	private int _lifetime = DEFAULT_LIFETIME;	/** The maximal allowed size. */	private int _maxsize = DEFAULT_MAXSIZE;	/** The reference queue. */	private transient ReferenceQueue _que;	/** The reference. */	private transient WeakReference _ref;	/** A flag used for debug purpose. */	private transient boolean _inExpunge;	/** The class to be hold in the reference (to know GC is demanding). */	private static class X {	}	/** The class to hold key/value. */	protected static final class Value implements java.io.Serializable, Cloneable {		private Object value;		private long access; //when the mapping is accessed		/** Creates an instance to store in the map. */		private Value(Object value) {			this.value = value;			updateAccessTime();		}		private final void updateAccessTime() {			this.access = System.currentTimeMillis();		}		//-- utilities--//		/** Returns the value. */		public final Object getValue() {			return this.value;		}		/** Returns the last access time. */		public final long getAccessTime() {			return this.access;		}		//-- cloneable --//		public Object clone() {			try {				return super.clone();			} catch (CloneNotSupportedException e) {				throw new InternalError();			}		}		//-- Object --//		public final String toString() {			return "(" + this.value + '@' + this.access + ')';		}	}	//-- deriving to override --//	/**	 * Called when a pair of key and value having been expunged.	 * This method is called after it is removed, so you could	 * add it back.	 *	 * <p>Default: does nothing	 */	protected void onExpunge(Value v) {	}	/** Returns by {@link #canExpunge} to denote it shall not be expunged. */	protected static final int EXPUNGE_NO = 0x0;	/** Returns by {@link #canExpunge} to denote it shall be expunged. */	protected static final int EXPUNGE_YES = 0x1; //must not zero	/** Returns by {@link #canExpunge} to denote the searching of the	 * next mapping shall continue.	 */	protected static final int EXPUNGE_CONTINUE = 0x0;	/** Returns by {@link #canExpunge} to denote the searching of the	 * next mapping shall stop.	 */	protected static final int EXPUNGE_STOP = 0x2; //must not zero	/** Returns whether it is time to expunge.	 * Once shallExpunge returns true, values are examined one-by-one thru	 * {@link #canExpunge}, and expunged if EXPUNGE_YES.	 *	 * <p>This implementation returns true only if {@link #newQueue}	 * returns null (in constructor) or GC was activated.	 * You might override it to enforce expunge besides GC.	 *	 * @see #canExpunge	 */	protected boolean shallExpunge() {		return _que == null || _que.poll() != null;	}	/**	 * Tests whether certain value is OK to expunge.	 *	 * <p>Note: values are tested thru {@link #canExpunge} only if	 * {@link #shallExpunge} returns true.	 *	 * <p>Deriving classes might override this method to return different	 * value for different criteria.	 *	 * <p>The return value coulde be a combination of EXPUNGE_xxx.	 * One of EXPUNGE_YES and EXPUNGE_NO is returned to denote	 * whether to expunge the mapping. One of EXPUNGE_CONTINUE and	 * EXPUNGE_STOP is returned to denote whether to continue the	 * searching of the next mapping for expunging.	 *	 * <p>Normally, you return either (EXPUNGE_YES|EXPUNGE_CONTINUE)	 * or (EXPUNG_NO|EXPUNGE_STOP).	 * Notice that the mapping is queried in the last-access order.	 * Thus, you rarely needs to return (EXPUNGE_NO|EXPUNGE_CONTINUE)	 * unless the appropriate one might be out of this order.	 *	 * <p>This implementation compares the access time and size.	 * It returns (EXPUNGE_YES|EXPUNGE_CONTINUE) if OK, and	 * (EXPUNGE_NO|EXPUNGE_STOP) if not.	 *	 * @return a combination of EXPUNGE_xxx	 * @see #shallExpunge	 */	protected int canExpunge(Value v) {		return _map.size() > getMaxSize()			|| (System.currentTimeMillis() - v.access) > getLifetime() ?			(EXPUNGE_YES|EXPUNGE_CONTINUE): (EXPUNGE_NO|EXPUNGE_STOP);	}	private void expunge() {		if (shallExpunge()) {			if (_inExpunge)				throw new IllegalStateException("expung in expung?");			_inExpunge = true;			try {				int k = 5; //to speed up, limit # to remove				for (final Iterator it = _map.values().iterator();				--k >= 0 && it.hasNext();) {					final Value v = (Value)it.next();					final int result = canExpunge(v);					if ((result & EXPUNGE_YES) != 0) {						//if (D.ON && log.debugable())						//	log.debug("expunge: value="+v.value+" size="+_map.size()+"("+getMaxSize()+") time="+v.access+"("+getLifetime()+")");						it.remove();						onExpunge(v);					}					if ((result & EXPUNGE_STOP) != 0)						break; //stop				}				newRef();			} finally {				_inExpunge = false;			}		}	}	/** Creates the reference queue.	 * It is called only once in the constructor (so it is meaningless	 * to change the returned value after constructed).	 *	 * <p>Default: new ReferenceQueue();<br>	 * Override this method to return null if you want to expunge items	 * every time {@link #get} or {@link #put} is called -- not only GC	 * is activated.	 * In other words, if {@link #newQueue} returns null, {@link #shallExpunge}	 * always returns true (unless you override it too).	 */	protected ReferenceQueue newQueue() {		return new ReferenceQueue();	}	/** Re-create the reference so we can detect if GC was activated.	 */	private void newRef() {		if (_que != null)			_ref = new WeakReference(new X(), _que);	}	//-- constructors --//	/** Constructs a cachemap by using LinkedHashMap internally.	 */	public CacheMap() {		_map = new LinkedHashMap(16, 0.75f, true);		init();	}	/** Constructs a cachemap by using LinkedHashMap internally.	 */	public CacheMap(int cap) {		_map = new LinkedHashMap(cap, 0.75f, true);		init();	}	/** Constructs a cachemap by using LinkedHashMap internally.	 */	public CacheMap(int cap, float load) {		_map = new LinkedHashMap(cap, load, true);		init();	}	/** Initialization for contructor and de-serialized. */	private void init() {		_que = newQueue();		newRef();	}	//-- extra api --//	/**	 * Gets the minimal lifetime, unit=milliseconds.	 * An mapping won't be removed by GC unless the minimal lifetime	 * or the maximal allowed size exceeds.	 * @see #getMaxSize	 */	public int getLifetime() {		return _lifetime;	}	/**	 * Sets the minimal lifetime. Default: {@link #DEFAULT_LIFETIME}.	 *	 * @param lifetime the lifetime, unit=milliseconds;	 * if non-posive, they will be removed immediately.	 * @see #getLifetime	 */	public CacheMap setLifetime(int lifetime) {

⌨️ 快捷键说明

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