/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.collection;
import java.util.HashMap;
import java.util.Map;
/**
* Reference-counting map based on a HashMap implementation that stores as a value a pair of value and reference counter.
* The class provides a reference method that takes a key
* and increments the reference count for the key. It also provides a de-reference method that takes a key and
* decrements the reference count for the key, and removes the key if the reference count reached zero.
* Null values are not allowed as keys.
*/
public class RefCountedMap<K,V>
{
private Map<K, Pair<V, Integer>> refMap;
/**
* Constructor.
*/
public RefCountedMap()
{
refMap = new HashMap<K, Pair<V, Integer>>();
}
/**
* Add and key and value with a reference count as one. If the key already exists, throw an exception.
* Clients should use the "get" method first to check if the key exists.
* @param key to add
* @param value to add
* @return value added
*/
public V put(K key, V value)
{
if (key == null)
{
throw new IllegalArgumentException("Collection does not allow null key values");
}
if (refMap.containsKey(key))
{
throw new IllegalStateException("Key value already in collection");
}
Pair<V, Integer> refValue = new Pair<V, Integer>(value, 1);
refMap.put(key, refValue);
return value;
}
/**
* Get the value for a given key, returning null if the key was not found.
* @param key is the key to look up and return the value for
* @return value for key, or null if key was not found
*/
public V get(K key)
{
Pair<V, Integer> refValue = refMap.get(key);
if (refValue == null)
{
return null;
}
return refValue.getFirst();
}
/**
* Increase the reference count for a given key by one.
* Throws an IllegalArgumentException if the key was not found.
* @param key is the key to increase the ref count for
*/
public void reference(K key)
{
Pair<V, Integer> refValue = refMap.get(key);
if (refValue == null)
{
throw new IllegalStateException("Key value not found in collection");
}
refValue.setSecond(refValue.getSecond() + 1);
}
/**
* Decreases the reference count for a given key by one. Returns true if the reference count reaches zero.
* Removes the key from the collection when the reference count reaches zero.
* Throw an IllegalArgumentException if the key is not found.
* @param key to de-reference
* @return true to indicate the reference count reached zero, false to indicate more references to the key exist.
*/
public boolean dereference(K key)
{
Pair<V, Integer> refValue = refMap.get(key);
if (refValue == null)
{
throw new IllegalStateException("Key value not found in collection");
}
int refCounter = refValue.getSecond();
if (refCounter < 1)
{
throw new IllegalStateException("Unexpected reference counter value " + refValue.getSecond() +
" encountered for key " + key);
}
// Remove key on dereference of last reference
if (refCounter == 1)
{
refMap.remove(key);
return true;
}
refValue.setSecond(refCounter - 1);
return false;
}
/**
* Clear out the collection.
*/
public void clear()
{
refMap.clear();
}
}