mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 04:22:05 +01:00
Generics
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@6331 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
5273e6c73d
commit
f40cb8bfbf
2 changed files with 94 additions and 43 deletions
|
@ -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();
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return it.hasNext();
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(Arrays.asList(values));
|
|
||||||
|
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 synchronized Set keySet() {
|
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<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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue