package com.limegroup.gnutella.util; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * This is a Set forgetting entries after a certain amount of time and it never * holds more entries than specified in the constructor. * * @author Gregorio Roper * * Note: do not use this class for time-sensitive operations. * if you do, wait at least 10-20ms before each operation. --zab */ public class FixedSizeExpiringSet implements Set, Collection { /* * Default size for the FixedSizExpiringSet */ private static final int DEFAULT_SIZE = 50; /* * Default time after which the entires expire 10 minutes */ private static final long DEFAULT_EXPIRE_TIME = 10 * 60 * 1000; private final int _maxSize; private final long _expireTime; private Map _map; /** * Simple constructor for the FixedSizeExpiringSet. Takes no arguments. */ public FixedSizeExpiringSet() { this(DEFAULT_SIZE); } /** * Constructor for the FixedSizeExpiringSet. * * @param size the max size of the set */ public FixedSizeExpiringSet(int size) { this(size, DEFAULT_EXPIRE_TIME); } /** * Constructor for the FixedSizeExpiringSet * * @param size the max size of the set * @param expireTime the time to keep an entry */ public FixedSizeExpiringSet(int size, long expireTime) { _maxSize = size; _expireTime = expireTime; _map = new HashMap(); } /* * (non-Javadoc) * * @see java.util.Collection#size() */ public int size() { expire(false); return _map.size(); } /* * (non-Javadoc) * * @see java.util.Collection#isEmpty() */ public boolean isEmpty() { return _map.isEmpty(); } /* * (non-Javadoc) * * @see java.util.Collection#contains(java.lang.Object) */ public boolean contains(Object arg0) { Long time = (Long) _map.get(arg0); if (time == null) return false; else if (time.longValue() < System.currentTimeMillis()) { _map.remove(arg0); return false; } else return true; } /* * (non-Javadoc) * * @see java.util.Collection#iterator() */ public Iterator iterator() { expire(false); return _map.keySet().iterator(); } /* * (non-Javadoc) * * @see java.util.Collection#toArray() */ public Object[] toArray() { expire(false); return _map.keySet().toArray(); } /* * (non-Javadoc) * * @see java.util.Collection#toArray(java.lang.Object[]) */ public Object[] toArray(Object[] arg0) { expire(false); return _map.keySet().toArray(arg0); } /* * (non-Javadoc) * * @see java.util.Collection#add(java.lang.Object) */ public boolean add(Object arg0) { if (arg0 == null) return false; expire(size() >= _maxSize); if (_map.containsKey(arg0)) //contract requires it! return false; _map.put(arg0, new Long(System.currentTimeMillis() + _expireTime)); return true; } /* * (non-Javadoc) * * @see java.util.Collection#remove(java.lang.Object) */ public boolean remove(Object arg0) { if (_map.remove(arg0) != null) return true; return false; } /** * (non-Javadoc) * * @see java.util.Collection#containsAll * (java.util.Collection) */ public boolean containsAll(Collection arg0) { return _map.keySet().containsAll(arg0); } /** * Adds all the elements in collection to this. If the size of the * collection is bigger than _maxSize only the first _maxSize elements are * added. * * @see java.util.Collection#addAll * (java.util.Collection) */ public boolean addAll(Collection coll) { if (coll.isEmpty()) return false; int i = 0; for (Iterator iter=coll.iterator(); i < _maxSize && iter.hasNext(); i++) add(iter.next()); return true; } /** * @see java.util.Collection#retainAll * (java.util.Collection) */ public boolean retainAll(Collection arg0) { Map map = new HashMap(); boolean ret = false; for (Iterator iter = _map.keySet().iterator(); iter.hasNext();) { Object o = iter.next(); if (arg0.contains(o)) map.put(o, _map.get(o)); else ret = true; } if (ret) _map = map; return ret; } /* * (non-Javadoc) * * @see java.util.Collection#removeAll * (java.util.Collection) */ public boolean removeAll(Collection arg0) { if (arg0.isEmpty()) return false; boolean ret = false; for (Iterator iter = arg0.iterator(); iter.hasNext();) ret |= remove(iter.next()); return ret; } /* * (non-Javadoc) * * @see java.util.Collection#clear() */ public void clear() { _map.clear(); } private void expire(boolean forceRemove) { if (_map.size() == 0) return; long now = System.currentTimeMillis(); long min = Long.MAX_VALUE; Object oldest = null; Collection expired = new HashSet(); for (Iterator iter = _map.keySet().iterator(); iter.hasNext();) { Object key = iter.next(); Long l = ((Long) _map.get(key)); long time = l.longValue(); if (time < now) { expired.add(key); forceRemove = false; } else if (forceRemove && time < min) { min = time; oldest = key; } } if (expired.size() > 0) removeAll(expired); if (forceRemove) remove(oldest); } }