/**
* Copyright (c) Rich Hickey. All rights reserved.
* The use and distribution terms for this software are covered by the
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
* which can be found in the file epl-v10.html at the root of this distribution.
* By using this software in any fashion, you are agreeing to be bound by
* the terms of this license.
* You must not remove this notice, or any other, from this software.
**/
/* rich Jul 31, 2008 */
package clojure.lang;
import java.util.concurrent.ConcurrentMap;
import java.util.*;
public class TransactionalHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>{
final Ref[] bins;
IPersistentMap mapAt(int bin){
return (IPersistentMap) bins[bin].deref();
}
final int binFor(Object k){
//spread hashes, a la Cliff Click
int h = k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
h ^= (h >>> 7) ^ (h >>> 4);
return h % bins.length;
// return k.hashCode() % bins.length;
}
Entry entryAt(Object k){
return mapAt(binFor(k)).entryAt(k);
}
public TransactionalHashMap() throws Exception{
this(421);
}
public TransactionalHashMap(int nBins) throws Exception{
bins = new Ref[nBins];
for(int i = 0; i < nBins; i++)
bins[i] = new Ref(PersistentHashMap.EMPTY);
}
public TransactionalHashMap(Map<? extends K, ? extends V> m) throws Exception{
this(m.size());
putAll(m);
}
public int size(){
int n = 0;
for(int i = 0; i < bins.length; i++)
{
n += mapAt(i).count();
}
return n;
}
public boolean isEmpty(){
return size() == 0;
}
public boolean containsKey(Object k){
return entryAt(k) != null;
}
public V get(Object k){
Entry e = entryAt(k);
if(e != null)
return (V) e.getValue();
return null;
}
public V put(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Object ret = map.valAt(k);
r.set(map.assoc(k, v));
return (V) ret;
}
public V remove(Object k){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Object ret = map.valAt(k);
//checked exceptions are a bad idea, especially in an interface
try
{
r.set(map.without(k));
}
catch(Exception e)
{
throw new RuntimeException(e);
}
return (V) ret;
}
public void putAll(Map<? extends K, ? extends V> map){
for(Iterator i = map.entrySet().iterator(); i.hasNext();)
{
Entry<K, V> e = (Entry) i.next();
put(e.getKey(), e.getValue());
}
}
public void clear(){
for(int i = 0; i < bins.length; i++)
{
Ref r = bins[i];
IPersistentMap map = (IPersistentMap) r.deref();
if(map.count() > 0)
{
r.set(PersistentHashMap.EMPTY);
}
}
}
public Set<Entry<K, V>> entrySet(){
final ArrayList<Map.Entry<K, V>> entries = new ArrayList(bins.length);
for(int i = 0; i < bins.length; i++)
{
IPersistentMap map = mapAt(i);
if(map.count() > 0)
entries.addAll((Collection) RT.seq(map));
}
return new AbstractSet<Entry<K, V>>(){
public Iterator iterator(){
return Collections.unmodifiableList(entries).iterator();
}
public int size(){
return entries.size();
}
};
}
public V putIfAbsent(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e == null)
{
r.set(map.assoc(k, v));
return null;
}
else
return (V) e.getValue();
}
public boolean remove(Object k, Object v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null && e.getValue().equals(v))
{
//checked exceptions are a bad idea, especially in an interface
try
{
r.set(map.without(k));
}
catch(Exception ex)
{
throw new RuntimeException(ex);
}
return true;
}
return false;
}
public boolean replace(K k, V oldv, V newv){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null && e.getValue().equals(oldv))
{
r.set(map.assoc(k, newv));
return true;
}
return false;
}
public V replace(K k, V v){
Ref r = bins[binFor(k)];
IPersistentMap map = (IPersistentMap) r.deref();
Entry e = map.entryAt(k);
if(e != null)
{
r.set(map.assoc(k, v));
return (V) e.getValue();
}
return null;
}
}