view src/goodjava/util/CacheMap.java @ 1778:1725fdb6c4f5

minor
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 11 Aug 2023 10:44:56 -0600
parents fa066aaa068c
children
line wrap: on
line source

package goodjava.util;

import java.lang.ref.ReferenceQueue;
import java.util.Map;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Set;
import java.util.AbstractSet;
import java.util.Iterator;


public abstract class CacheMap<K,V> extends AbstractMap<K,V> {

	protected static interface MyReference<K,V> {
		public K key();
		public V get();
		public void clear();
	}

	protected abstract MyReference<K,V> newReference(K key,V value,ReferenceQueue<V> q);
 
	private final Map<K,MyReference<K,V>> cache = new HashMap<K,MyReference<K,V>>();
	private final ReferenceQueue<V> queue = new ReferenceQueue<V>();

	private void sweep() {
		while(true) {
			@SuppressWarnings("unchecked")
			MyReference<K,V> ref = (MyReference<K,V>)queue.poll();
			if( ref == null )
				return;
			MyReference<K,V> mappedRef = cache.remove(ref.key());
			if( mappedRef != ref && mappedRef != null )
				cache.put( mappedRef.key(), mappedRef );  // put it back
		}
	}

	@Override public int size() {
		return cache.size();
	}

	@Override public boolean isEmpty() {
		return cache.isEmpty();
	}

	@Override public boolean containsKey(Object key) {
		return cache.containsKey(key);
	}

	@Override public V get(Object key) {
		MyReference<K,V> ref = cache.get(key);
		return ref==null ? null : ref.get();
	}

	@Override public V put(K key,V value) {
		sweep();
		MyReference<K,V> ref = cache.put( key, newReference(key,value,queue) );
		return ref==null ? null : ref.get();
	}

	@Override public V remove(Object key) {
		sweep();
		MyReference<K,V> ref = cache.remove(key);
		return ref==null ? null : ref.get();
	}

	@Override public void clear() {
		sweep();
		cache.clear();
	}

/*
	public Object clone() {
		GCCacheMap map = new GCCacheMap();
		map.cache = (HashMap)cache.clone();
		return map;
	}
*/
	@Override public Set<K> keySet() {
		return cache.keySet();
	}

	@Override public Set<Map.Entry<K,V>> entrySet() {
		return new MySet();
	}


	private class MySet extends AbstractSet<Map.Entry<K,V>> {

		@Override public int size() {
			return CacheMap.this.size();
		}

		@Override public Iterator<Map.Entry<K,V>> iterator() {
			return new MyIterator();
		}

	}

	private class MyIterator implements Iterator<Map.Entry<K,V>> {
		final Iterator<Map.Entry<K,MyReference<K,V>>> iter = cache.entrySet().iterator();

		@Override public boolean hasNext() {
			return iter.hasNext();
		}

		@Override public void remove() {
			iter.remove();
		}

		@Override public Map.Entry<K,V> next() {
			return new MyEntry( iter.next() );
		}
	}

	private class MyEntry implements Map.Entry<K,V> {
		Map.Entry<K,MyReference<K,V>> entry;

		MyEntry(Map.Entry<K,MyReference<K,V>> entry) {
			this.entry = entry;
		}

		@Override public K getKey() {
			return entry.getKey();
		}

		@Override public V getValue() {
			MyReference<K,V> ref = entry.getValue();
			return ref.get();
		}

		@Override public V setValue(V value) {
			MyReference<K,V> ref = entry.setValue( newReference(getKey(),value,queue) );
			return ref.get();
		}

		@Override public boolean equals(Object o) {
			if( o==null || !(o instanceof CacheMap.MyEntry) )
				return false;
			@SuppressWarnings("unchecked")
			MyEntry m = (MyEntry)o;
			return entry.equals(m.entry);
		}

		@Override public int hashCode() {
			K key = getKey();
			V value = getValue();
			return (key==null ? 0 : key.hashCode()) ^
					(value==null ? 0 : value.hashCode());
		}
	}

}