git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6331 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
Alex Wenckus 2006-12-07 03:25:39 +00:00 committed by alex
parent 5273e6c73d
commit f40cb8bfbf
2 changed files with 94 additions and 43 deletions

View File

@ -19,6 +19,8 @@
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import org.jivesoftware.smack.util.collections.AbstractMapEntry;
import java.util.*; import java.util.*;
/** /**
@ -46,12 +48,12 @@ import java.util.*;
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
public class Cache implements Map { public class Cache<K, V> implements Map<K, V> {
/** /**
* The map the keys and values are stored in. * The map the keys and values are stored in.
*/ */
protected Map map; protected Map<K, CacheObject<V>> map;
/** /**
* Linked list to maintain order that cache objects are accessed * Linked list to maintain order that cache objects are accessed
@ -103,20 +105,20 @@ public class Cache implements Map {
// Our primary data structure is a hash map. The default capacity of 11 // Our primary data structure is a hash map. The default capacity of 11
// is too small in almost all cases, so we set it bigger. // is too small in almost all cases, so we set it bigger.
map = new HashMap(103); map = new HashMap<K, CacheObject<V>>(103);
lastAccessedList = new LinkedList(); lastAccessedList = new LinkedList();
ageList = new LinkedList(); ageList = new LinkedList();
} }
public synchronized Object put(Object key, Object value) { public synchronized V put(K key, V value) {
Object oldValue = null; V oldValue = null;
// Delete an old entry if it exists. // Delete an old entry if it exists.
if (map.containsKey(key)) { if (map.containsKey(key)) {
oldValue = remove(key, true); oldValue = remove(key, true);
} }
CacheObject cacheObject = new CacheObject(value); CacheObject<V> cacheObject = new CacheObject<V>(value);
map.put(key, cacheObject); map.put(key, cacheObject);
// Make an entry into the cache order list. // Make an entry into the cache order list.
// Store the cache order list entry so that we can get back to it // Store the cache order list entry so that we can get back to it
@ -133,12 +135,12 @@ public class Cache implements Map {
return oldValue; return oldValue;
} }
public synchronized Object get(Object key) { public synchronized V get(Object key) {
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
CacheObject cacheObject = (CacheObject) map.get(key); CacheObject<V> cacheObject = map.get(key);
if (cacheObject == null) { if (cacheObject == null) {
// The object didn't exist in cache, so increment cache misses. // The object didn't exist in cache, so increment cache misses.
cacheMisses++; cacheMisses++;
@ -157,7 +159,7 @@ public class Cache implements Map {
return cacheObject.object; return cacheObject.object;
} }
public synchronized Object remove(Object key) { public synchronized V remove(Object key) {
return remove(key, false); return remove(key, false);
} }
@ -165,8 +167,9 @@ public class Cache implements Map {
* Remove operation with a flag so we can tell coherence if the remove was * Remove operation with a flag so we can tell coherence if the remove was
* caused by cache internal processing such as eviction or loading * caused by cache internal processing such as eviction or loading
*/ */
public synchronized Object remove(Object key, boolean internal) { public synchronized V remove(Object key, boolean internal) {
CacheObject cacheObject = (CacheObject) map.remove(key); //noinspection SuspiciousMethodCalls
CacheObject<V> cacheObject = map.remove(key);
// If the object is not in cache, stop trying to remove it. // If the object is not in cache, stop trying to remove it.
if (cacheObject == null) { if (cacheObject == null) {
return null; return null;
@ -183,8 +186,8 @@ public class Cache implements Map {
public synchronized void clear() { public synchronized void clear() {
Object[] keys = map.keySet().toArray(); Object[] keys = map.keySet().toArray();
for (int i = 0; i < keys.length; i++) { for (Object key : keys) {
remove(keys[i]); remove(key);
} }
// Now, reset all containers. // Now, reset all containers.
@ -212,17 +215,35 @@ public class Cache implements Map {
return map.isEmpty(); return map.isEmpty();
} }
public synchronized Collection values() { public synchronized Collection<V> values() {
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
Object[] cacheObjects = map.values().toArray(); return Collections.unmodifiableCollection(new AbstractCollection<V>() {
Object[] values = new Object[cacheObjects.length]; Collection<CacheObject<V>> values = map.values();
for (int i = 0; i < cacheObjects.length; i++) { public Iterator<V> iterator() {
values[i] = ((CacheObject) cacheObjects[i]).object; return new Iterator<V>() {
} Iterator<CacheObject<V>> it = values.iterator();
return Collections.unmodifiableList(Arrays.asList(values));
public boolean hasNext() {
return it.hasNext();
}
public V next() {
return it.next().object;
}
public void remove() {
it.remove();
}
};
}
public int size() {
return values.size();
}
});
} }
public synchronized boolean containsKey(Object key) { public synchronized boolean containsKey(Object key) {
@ -233,15 +254,15 @@ public class Cache implements Map {
return map.containsKey(key); return map.containsKey(key);
} }
public void putAll(Map map) { public void putAll(Map<? extends K, ? extends V> map) {
for (Iterator i = map.entrySet().iterator(); i.hasNext();) { for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
Map.Entry entry = (Map.Entry) i.next(); V value = entry.getValue();
Object value = entry.getValue();
// If the map is another DefaultCache instance than the // If the map is another DefaultCache instance than the
// entry values will be CacheObject instances that need // entry values will be CacheObject instances that need
// to be converted to the normal object form. // to be converted to the normal object form.
if (value instanceof CacheObject) { if (value instanceof CacheObject) {
value = ((CacheObject) value).object; //noinspection unchecked
value = ((CacheObject<V>) value).object;
} }
put(entry.getKey(), value); put(entry.getKey(), value);
} }
@ -252,12 +273,13 @@ public class Cache implements Map {
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
CacheObject cacheObject = new CacheObject(value); //noinspection unchecked
CacheObject<V> cacheObject = new CacheObject<V>((V) value);
return map.containsValue(cacheObject); return map.containsValue(cacheObject);
} }
public synchronized Set entrySet() { public synchronized Set<Map.Entry<K, V>> entrySet() {
// Warning -- this method returns CacheObject instances and not Objects // Warning -- this method returns CacheObject instances and not Objects
// in the same form they were put into cache. // in the same form they were put into cache.
@ -265,10 +287,40 @@ public class Cache implements Map {
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
return Collections.unmodifiableSet(map.entrySet()); return new AbstractSet<Map.Entry<K, V>>() {
private final Set<Map.Entry<K, CacheObject<V>>> set = map.entrySet();
public Iterator<Entry<K, V>> iterator() {
return new Iterator<Entry<K, V>>() {
private final Iterator<Entry<K, CacheObject<V>>> it = set.iterator();
public boolean hasNext() {
return it.hasNext();
}
public Entry<K, V> next() {
Map.Entry<K, CacheObject<V>> entry = it.next();
return new AbstractMapEntry<K, V>(entry.getKey(), entry.getValue().object) {
@Override
public V setValue(V value) {
throw new UnsupportedOperationException("Cannot set");
}
};
}
public void remove() {
it.remove();
}
};
}
public int size() {
return set.size();
}
};
} }
public synchronized Set keySet() { public synchronized Set<K> keySet() {
// First, clear all entries that have been in cache longer than the // First, clear all entries that have been in cache longer than the
// maximum defined age. // maximum defined age.
deleteExpiredEntries(); deleteExpiredEntries();
@ -381,12 +433,12 @@ public class Cache implements Map {
* *
* This class is optimized for speed rather than strictly correct encapsulation. * This class is optimized for speed rather than strictly correct encapsulation.
*/ */
private static class CacheObject { private static class CacheObject<V> {
/** /**
* Underlying object wrapped by the CacheObject. * Underlying object wrapped by the CacheObject.
*/ */
public Object object; public V object;
/** /**
* A reference to the node in the cache order list. We keep the reference * A reference to the node in the cache order list. We keep the reference
@ -413,7 +465,7 @@ public class Cache implements Map {
* *
* @param object the underlying Object to wrap. * @param object the underlying Object to wrap.
*/ */
public CacheObject(Object object) { public CacheObject(V object) {
this.object = object; this.object = object;
} }
@ -427,11 +479,8 @@ public class Cache implements Map {
final CacheObject cacheObject = (CacheObject) o; final CacheObject cacheObject = (CacheObject) o;
if (!object.equals(cacheObject.object)) { return object.equals(cacheObject.object);
return false;
}
return true;
} }
public int hashCode() { public int hashCode() {
@ -489,6 +538,7 @@ public class Cache implements Map {
* Adds a node to the beginning of the list. * Adds a node to the beginning of the list.
* *
* @param node the node to add to the beginning of the list. * @param node the node to add to the beginning of the list.
* @return the node
*/ */
public LinkedListNode addFirst(LinkedListNode node) { public LinkedListNode addFirst(LinkedListNode node) {
node.next = head.next; node.next = head.next;

View File

@ -60,18 +60,19 @@ import junit.framework.TestCase;
public class CacheTest extends TestCase { public class CacheTest extends TestCase {
public void testMaxSize() { public void testMaxSize() {
Cache cache = new Cache(100, -1); Cache<Integer, String> cache = new Cache<Integer, String>(100, -1);
for (int i=0; i<1000; i++) { for (int i=0; i < 1000; i++) {
cache.put(new Integer(i), "value"); cache.put(i, "value");
assertTrue("Cache size must never be larger than 100.", cache.size() <= 100); assertTrue("Cache size must never be larger than 100.", cache.size() <= 100);
} }
} }
public void testLRU() { public void testLRU() {
Cache cache = new Cache(100, -1); Cache<Integer, String> cache = new Cache<Integer, String>(100, -1);
for (int i=0; i<1000; i++) { for (int i=0; i < 1000; i++) {
cache.put(new Integer(i), "value"); cache.put(i, "value");
assertTrue("LRU algorithm for cache key of '0' failed.", cache.get(new Integer(0)) != null); assertTrue("LRU algorithm for cache key of '0' failed.",
cache.get(new Integer(0)) != null);
} }
} }
} }