| 1498 | 1 package goodjava.util; | 
|  | 2 | 
|  | 3 import java.lang.ref.ReferenceQueue; | 
|  | 4 import java.util.Map; | 
|  | 5 import java.util.AbstractMap; | 
|  | 6 import java.util.HashMap; | 
|  | 7 import java.util.Set; | 
|  | 8 import java.util.AbstractSet; | 
|  | 9 import java.util.Iterator; | 
|  | 10 | 
|  | 11 | 
|  | 12 public abstract class CacheMap<K,V> extends AbstractMap<K,V> { | 
|  | 13 | 
|  | 14 	protected static interface MyReference<K,V> { | 
|  | 15 		public K key(); | 
|  | 16 		public V get(); | 
|  | 17 		public void clear(); | 
|  | 18 	} | 
|  | 19 | 
|  | 20 	protected abstract MyReference<K,V> newReference(K key,V value,ReferenceQueue<V> q); | 
|  | 21 | 
|  | 22 	private final Map<K,MyReference<K,V>> cache = new HashMap<K,MyReference<K,V>>(); | 
|  | 23 	private final ReferenceQueue<V> queue = new ReferenceQueue<V>(); | 
|  | 24 | 
|  | 25 	private void sweep() { | 
|  | 26 		while(true) { | 
|  | 27 			@SuppressWarnings("unchecked") | 
|  | 28 			MyReference<K,V> ref = (MyReference<K,V>)queue.poll(); | 
|  | 29 			if( ref == null ) | 
|  | 30 				return; | 
|  | 31 			MyReference<K,V> mappedRef = cache.remove(ref.key()); | 
|  | 32 			if( mappedRef != ref && mappedRef != null ) | 
|  | 33 				cache.put( mappedRef.key(), mappedRef );  // put it back | 
|  | 34 		} | 
|  | 35 	} | 
|  | 36 | 
| 1607 | 37 	@Override public int size() { | 
| 1498 | 38 		return cache.size(); | 
|  | 39 	} | 
|  | 40 | 
| 1607 | 41 	@Override public boolean isEmpty() { | 
| 1498 | 42 		return cache.isEmpty(); | 
|  | 43 	} | 
|  | 44 | 
| 1607 | 45 	@Override public boolean containsKey(Object key) { | 
| 1498 | 46 		return cache.containsKey(key); | 
|  | 47 	} | 
|  | 48 | 
| 1607 | 49 	@Override public V get(Object key) { | 
| 1498 | 50 		MyReference<K,V> ref = cache.get(key); | 
|  | 51 		return ref==null ? null : ref.get(); | 
|  | 52 	} | 
|  | 53 | 
| 1607 | 54 	@Override public V put(K key,V value) { | 
| 1498 | 55 		sweep(); | 
|  | 56 		MyReference<K,V> ref = cache.put( key, newReference(key,value,queue) ); | 
|  | 57 		return ref==null ? null : ref.get(); | 
|  | 58 	} | 
|  | 59 | 
| 1607 | 60 	@Override public V remove(Object key) { | 
| 1498 | 61 		sweep(); | 
|  | 62 		MyReference<K,V> ref = cache.remove(key); | 
|  | 63 		return ref==null ? null : ref.get(); | 
|  | 64 	} | 
|  | 65 | 
| 1607 | 66 	@Override public void clear() { | 
| 1498 | 67 		sweep(); | 
|  | 68 		cache.clear(); | 
|  | 69 	} | 
|  | 70 | 
|  | 71 /* | 
|  | 72 	public Object clone() { | 
|  | 73 		GCCacheMap map = new GCCacheMap(); | 
|  | 74 		map.cache = (HashMap)cache.clone(); | 
|  | 75 		return map; | 
|  | 76 	} | 
|  | 77 */ | 
| 1607 | 78 	@Override public Set<K> keySet() { | 
| 1498 | 79 		return cache.keySet(); | 
|  | 80 	} | 
|  | 81 | 
| 1607 | 82 	@Override public Set<Map.Entry<K,V>> entrySet() { | 
| 1498 | 83 		return new MySet(); | 
|  | 84 	} | 
|  | 85 | 
|  | 86 | 
|  | 87 	private class MySet extends AbstractSet<Map.Entry<K,V>> { | 
|  | 88 | 
| 1607 | 89 		@Override public int size() { | 
| 1498 | 90 			return CacheMap.this.size(); | 
|  | 91 		} | 
|  | 92 | 
| 1607 | 93 		@Override public Iterator<Map.Entry<K,V>> iterator() { | 
|  | 94 			return new MyIterator(); | 
| 1498 | 95 		} | 
|  | 96 | 
|  | 97 	} | 
|  | 98 | 
|  | 99 	private class MyIterator implements Iterator<Map.Entry<K,V>> { | 
| 1607 | 100 		final Iterator<Map.Entry<K,MyReference<K,V>>> iter = cache.entrySet().iterator(); | 
| 1498 | 101 | 
| 1607 | 102 		@Override public boolean hasNext() { | 
| 1498 | 103 			return iter.hasNext(); | 
|  | 104 		} | 
|  | 105 | 
| 1607 | 106 		@Override public void remove() { | 
| 1498 | 107 			iter.remove(); | 
|  | 108 		} | 
|  | 109 | 
| 1607 | 110 		@Override public Map.Entry<K,V> next() { | 
| 1498 | 111 			return new MyEntry( iter.next() ); | 
|  | 112 		} | 
|  | 113 	} | 
|  | 114 | 
|  | 115 	private class MyEntry implements Map.Entry<K,V> { | 
|  | 116 		Map.Entry<K,MyReference<K,V>> entry; | 
|  | 117 | 
|  | 118 		MyEntry(Map.Entry<K,MyReference<K,V>> entry) { | 
|  | 119 			this.entry = entry; | 
|  | 120 		} | 
|  | 121 | 
| 1607 | 122 		@Override public K getKey() { | 
| 1498 | 123 			return entry.getKey(); | 
|  | 124 		} | 
|  | 125 | 
| 1607 | 126 		@Override public V getValue() { | 
| 1498 | 127 			MyReference<K,V> ref = entry.getValue(); | 
|  | 128 			return ref.get(); | 
|  | 129 		} | 
|  | 130 | 
| 1607 | 131 		@Override public V setValue(V value) { | 
| 1498 | 132 			MyReference<K,V> ref = entry.setValue( newReference(getKey(),value,queue) ); | 
|  | 133 			return ref.get(); | 
|  | 134 		} | 
|  | 135 | 
| 1607 | 136 		@Override public boolean equals(Object o) { | 
| 1498 | 137 			if( o==null || !(o instanceof CacheMap.MyEntry) ) | 
|  | 138 				return false; | 
|  | 139 			@SuppressWarnings("unchecked") | 
|  | 140 			MyEntry m = (MyEntry)o; | 
|  | 141 			return entry.equals(m.entry); | 
|  | 142 		} | 
|  | 143 | 
| 1607 | 144 		@Override public int hashCode() { | 
| 1498 | 145 			K key = getKey(); | 
|  | 146 			V value = getValue(); | 
|  | 147 			return (key==null ? 0 : key.hashCode()) ^ | 
|  | 148 					(value==null ? 0 : value.hashCode()); | 
|  | 149 		} | 
|  | 150 	} | 
|  | 151 | 
|  | 152 } |