/* SnapTree - (c) 2009 Stanford University - PPL */ // LeafMap package trees.lockbased.stanfordutils; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceArray; /** This SnapHashMap has separate types for the branch and the leaf. Branches * extend AtomicReferenceArray, and leaves use Java monitors. */ public class SnapHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Cloneable, Serializable { private static final long serialVersionUID = 9212449426984968891L; private static final int LOG_BF = 8; private static final int BF = 1 << LOG_BF; private static final int BF_MASK = BF - 1; private static int maxLoad(final int capacity) { return capacity - (capacity >> 2); /* 0.75 */ } private static int minLoad(final int capacity) { return (capacity >> 2); /* 0.25 */ } // LEAF_MAX_CAPACITY * 2 / BF must be >= LEAF_MIN_CAPACITY private static final int LOG_LEAF_MIN_CAPACITY = 3; private static final int LOG_LEAF_MAX_CAPACITY = LOG_LEAF_MIN_CAPACITY + LOG_BF - 1; private static final int LEAF_MIN_CAPACITY = 1 << LOG_LEAF_MIN_CAPACITY; private static final int LEAF_MAX_CAPACITY = 1 << LOG_LEAF_MAX_CAPACITY; private static final int ROOT_SHIFT = 0; static class Generation { } static class HashEntry<K,V> { final Generation gen; final K key; final int hash; V value; final HashEntry<K,V> next; HashEntry(final Generation gen, final K key, final int hash, final V value, final HashEntry<K,V> next) { this.gen = gen; this.key = key; this.hash = hash; this.value = value; this.next = next; } HashEntry<K,V> withRemoved(final Generation gen, final HashEntry<K,V> target) { if (this == target) { return next; } else { return new HashEntry<K,V>(gen, key, hash, value, next.withRemoved(gen, target)); } } } /** A small hash table. The caller is responsible for synchronization in most * cases. */ static final class LeafMap<K,V> { /** Set to null when this LeafMap is split into pieces. */ Generation gen; HashEntry<K,V>[] table; /** The number of unique hash codes recorded in this LeafMap. This is also * used to establish synchronization order, by reading in containsKey and * get and writing in any updating function. We track unique hash codes * instead of entries because LeafMaps are split into multiple LeafMaps * when they grow too large. If we used a basic count, then if many keys * were present with the same hash code this splitting operation would not * help to restore the splitting condition. */ volatile int uniq; final int shift; @SuppressWarnings("unchecked") LeafMap(final Generation gen, final int shift) { this.gen = gen; this.table = (HashEntry<K,V>[]) new HashEntry[LEAF_MIN_CAPACITY]; this.uniq = 0; this.shift = shift; } @SuppressWarnings("unchecked") private LeafMap(final Generation gen, final LeafMap src) { this.gen = gen; this.table = (HashEntry<K,V>[]) src.table.clone(); this.uniq = src.uniq; this.shift = src.shift; } LeafMap cloneForWrite(final Generation gen) { return new LeafMap(gen, this); } boolean hasSplit() { return gen == null; } boolean containsKey(final Object key, final int hash) { if (uniq == 0) { // volatile read return false; } HashEntry<K,V> e = table[(hash >> shift) & (table.length - 1)]; while (e != null) { if (e.hash == hash && key.equals(e.key)) { return true; } e = e.next; } return false; } private synchronized V lockedReadValue(final HashEntry<K,V> e) { return e.value; } /** This is only valid for a quiesced map. */ boolean containsValue(final Object value) { for (HashEntry<K,V> head : table) { HashEntry<K,V> e = head; while (e != null) { V v = e.value; if (v == null) { v = lockedReadValue(e); } if (value.equals(v)) { return true; } e = e.next; } } return false; } V get(final Object key, final int hash) { if (uniq == 0) { // volatile read return null; } HashEntry<K,V> e = table[(hash >> shift) & (table.length - 1)]; while (e != null) { if (e.hash == hash && key.equals(e.key)) { final V v = e.value; if (v == null) { return lockedReadValue(e); } return v; } e = e.next; } return null; } private void growIfNecessary() { assert(!hasSplit()); final int n = table.length; if (n < LEAF_MAX_CAPACITY && uniq > maxLoad(n)) { rehash(n << 1); } } private void shrinkIfNecessary() { assert(!hasSplit()); final int n = table.length; if (n > LEAF_MIN_CAPACITY && uniq < minLoad(n)) { rehash(n >> 1); } } @SuppressWarnings("unchecked") private void rehash(final int newSize) { final HashEntry<K,V>[] prevTable = table; table = (HashEntry<K,V>[]) new HashEntry[newSize]; for (HashEntry<K,V> head : prevTable) { reputAll(head); } } private void reputAll(final HashEntry<K,V> head) { if (head != null) { reputAll(head.next); reput(head); } } private void reput(final HashEntry<K,V> e) { final int i = (e.hash >> shift) & (table.length - 1); final HashEntry<K,V> next = table[i]; if (e.next == next) { // no new entry needed table[i] = e; } else { table[i] = new HashEntry<K,V>(gen, e.key, e.hash, e.value, next); } } V put(final K key, final int hash, final V value) { growIfNecessary(); final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; int insDelta = 1; while (e != null) { if (e.hash == hash) { if (key.equals(e.key)) { // match final V prev = e.value; if (e.gen == gen) { // we have permission to mutate the node e.value = value; } else { // we must replace the node table[i] = new HashEntry<K,V>(gen, key, hash, value, head.withRemoved(gen, e)); } uniq = uniq; // volatile store return prev; } // Hash match, but not a key match. If we eventually insert, // then we won't modify uniq. insDelta = 0; } e = e.next; } // no match table[i] = new HashEntry<K,V>(gen, key, hash, value, head); uniq += insDelta; // volatile store return null; } V remove(final Object key, final int hash) { shrinkIfNecessary(); final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; int delDelta = -1; while (e != null) { if (e.hash == hash) { if (key.equals(e.key)) { // match final HashEntry<K,V> target = e; // continue the loop to get the right delDelta if (delDelta != 0) { e = e.next; while (e != null) { if (e.hash == hash) { delDelta = 0; break; } e = e.next; } } // match uniq += delDelta; // volatile store table[i] = head.withRemoved(gen, target); return target.value; } // hash match, but not key match delDelta = 0; } e = e.next; } // no match return null; } //////// CAS-like V putIfAbsent(final K key, final int hash, final V value) { growIfNecessary(); final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; int insDelta = 1; while (e != null) { if (e.hash == hash) { if (key.equals(e.key)) { // match => failure return e.value; } // Hash match, but not a key match. If we eventually insert, // then we won't modify uniq. insDelta = 0; } e = e.next; } // no match table[i] = new HashEntry<K,V>(gen, key, hash, value, head); uniq += insDelta; // volatile store return null; } boolean replace(final K key, final int hash, final V oldValue, final V newValue) { final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; while (e != null) { if (e.hash == hash && key.equals(e.key)) { // key match if (oldValue.equals(e.value)) { // CAS success if (e.gen == gen) { // we have permission to mutate the node e.value = newValue; } else { // we must replace the node table[i] = new HashEntry<K,V>(gen, key, hash, newValue, head.withRemoved(gen, e)); } uniq = uniq; // volatile store return true; } else { // CAS failure return false; } } e = e.next; } // no match return false; } V replace(final K key, final int hash, final V value) { final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; while (e != null) { if (e.hash == hash && key.equals(e.key)) { // match final V prev = e.value; if (e.gen == gen) { // we have permission to mutate the node e.value = value; } else { // we must replace the node table[i] = new HashEntry<K,V>(gen, key, hash, value, head.withRemoved(gen, e)); } uniq = uniq; // volatile store return prev; } e = e.next; } // no match return null; } boolean remove(final Object key, final int hash, final Object value) { shrinkIfNecessary(); final int i = (hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; HashEntry<K,V> e = head; int delDelta = -1; while (e != null) { if (e.hash == hash) { if (key.equals(e.key)) { // key match if (!value.equals(e.value)) { // CAS failure return false; } final HashEntry<K,V> target = e; // continue the loop to get the right delDelta if (delDelta != 0) { e = e.next; while (e != null) { if (e.hash == hash) { delDelta = 0; break; } e = e.next; } } // match uniq += delDelta; // volatile store table[i] = head.withRemoved(gen, target); return true; } // hash match, but not key match delDelta = 0; } e = e.next; } // no match return false; } //////// Leaf splitting boolean shouldSplit() { return uniq > maxLoad(LEAF_MAX_CAPACITY); } @SuppressWarnings("unchecked") LeafMap<K,V>[] split() { assert(!hasSplit()); final LeafMap<K,V>[] pieces = (LeafMap<K,V>[]) new LeafMap[BF]; for (int i = 0; i < pieces.length; ++i) { pieces[i] = new LeafMap<K,V>(gen, shift + LOG_BF); } for (HashEntry<K,V> head : table) { for (HashEntry<K,V> e = head; e != null; e = e.next) { final int i = (e.hash >> shift) & BF_MASK; pieces[i].put(e); } } gen = null; // this marks us as split return pieces; } private void put(final HashEntry<K,V> entry) { growIfNecessary(); final int i = (entry.hash >> shift) & (table.length - 1); final HashEntry<K,V> head = table[i]; // is this hash a duplicate? int uDelta = 1; for (HashEntry<K,V> e = head; e != null; e = e.next) { if (e.hash == entry.hash) { uDelta = 0; break; } } if (uDelta != 0) { uniq += uDelta; } if (entry.next == head) { // we can reuse the existing entry table[i] = entry; } else { table[i] = new HashEntry<K,V>(gen, entry.key, entry.hash, entry.value, head); } } } static final class BranchMap<K,V> extends AtomicReferenceArray<Object> { final Generation gen; final int shift; BranchMap(final Generation gen, final int shift) { super(BF); this.gen = gen; this.shift = shift; } BranchMap(final Generation gen, final int shift, final Object[] children) { super(children); this.gen = gen; this.shift = shift; } private BranchMap(final Generation gen, final BranchMap src) { super(BF); this.gen = gen; this.shift = src.shift; for (int i = 0; i < BF; ++i) { lazySet(i, src.get(i)); } } BranchMap<K,V> cloneForWrite(final Generation gen) { return new BranchMap<K,V>(gen, this); } @SuppressWarnings("unchecked") boolean containsKey(final Object key, final int hash) { final Object child = get(indexFor(hash)); if (child == null) { return false; } else if (child instanceof LeafMap) { return ((LeafMap<K,V>) child).containsKey(key, hash); } else { return ((BranchMap<K,V>) child).containsKey(key, hash); } } private int indexFor(final int hash) { return (hash >> shift) & BF_MASK; } /** This is only valid for a quiesced map. */ @SuppressWarnings("unchecked") boolean containsValueQ(final Object value) { for (int i = 0; i < BF; ++i) { final Object child = get(i); if (child != null) { if (child instanceof LeafMap) { if (((LeafMap<K,V>) child).containsValue(value)) { return true; } } else { if (((BranchMap<K,V>) child).containsValueQ(value)) { return true; } } } } return false; } @SuppressWarnings("unchecked") V get(final Object key, final int hash) { final Object child = get(indexFor(hash)); if (child == null) { return null; } else if (child instanceof LeafMap) { return ((LeafMap<K,V>) child).get(key, hash); } else { return ((BranchMap<K,V>) child).get(key, hash); } } @SuppressWarnings("unchecked") V put(final K key, final int hash, final V value) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.put(key, hash, value); } } } return unsharedBranch(index, child).put(key, hash, value); } private Object getOrCreateChild(final int index) { final Object result = get(index); return result != null ? result : createChild(index); } private Object createChild(final int index) { final LeafMap<K,V> repl = new LeafMap<K,V>(gen, shift + LOG_BF); if (compareAndSet(index, null, repl)) { // success return repl; } else { // if we failed, someone else succeeded return get(index); } } private Object prepareForLeafMutation(final int index, final LeafMap<K,V> leaf) { if (leaf.shouldSplit()) { if (leaf.hasSplit()) { // leaf was split between our getOrCreateChild and our lock, reread return get(index); } else { // no need to CAS, because everyone is using the lock final Object repl = new BranchMap<K,V>(gen, shift + LOG_BF, leaf.split()); lazySet(index, repl); return repl; } } else if (leaf.gen != gen) { // copy-on-write final Object repl = leaf.cloneForWrite(gen); lazySet(index, repl); return repl; } else { // OKAY return null; } } @SuppressWarnings("unchecked") private BranchMap<K,V> unsharedBranch(final int index, final Object child) { final BranchMap<K,V> branch = (BranchMap<K,V>) child; if (branch.gen == gen) { return branch; } else { final BranchMap<K,V> fresh = branch.cloneForWrite(gen); if (compareAndSet(index, child, fresh)) { return fresh; } else { // if we failed someone else succeeded return (BranchMap<K,V>) get(index); } } } @SuppressWarnings("unchecked") V remove(final Object key, final int hash) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.remove(key, hash); } } } return unsharedBranch(index, child).remove(key, hash); } //////// CAS-like @SuppressWarnings("unchecked") V putIfAbsent(final K key, final int hash, final V value) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.putIfAbsent(key, hash, value); } } } return unsharedBranch(index, child).putIfAbsent(key, hash, value); } @SuppressWarnings("unchecked") boolean replace(final K key, final int hash, final V oldValue, final V newValue) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.replace(key, hash, oldValue, newValue); } } } return unsharedBranch(index, child).replace(key, hash, oldValue, newValue); } @SuppressWarnings("unchecked") V replace(final K key, final int hash, final V value) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.replace(key, hash, value); } } } return unsharedBranch(index, child).replace(key, hash, value); } @SuppressWarnings("unchecked") boolean remove(final Object key, final int hash, final Object value) { final int index = indexFor(hash); Object child = getOrCreateChild(index); while (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; synchronized (leaf) { child = prepareForLeafMutation(index, leaf); if (child == null) { // no replacement was provided return leaf.remove(key, hash, value); } } } return unsharedBranch(index, child).remove(key, hash, value); } } static class COWMgr<K,V> extends CopyOnWriteManager<BranchMap<K,V>> { COWMgr() { super(new BranchMap<K,V>(new Generation(), ROOT_SHIFT), 0); } COWMgr(final BranchMap<K,V> initialValue, final int initialSize) { super(initialValue, initialSize); } protected BranchMap<K, V> freezeAndClone(final BranchMap<K,V> value) { return value.cloneForWrite(new Generation()); } protected BranchMap<K, V> cloneFrozen(final BranchMap<K,V> frozenValue) { return frozenValue.cloneForWrite(new Generation()); } } private transient volatile COWMgr<K,V> rootHolder; private static int hash(int h) { // taken from ConcurrentHashMap h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); return h ^ (h >>> 16); } //////// construction and cloning public SnapHashMap() { this.rootHolder = new COWMgr<K,V>(); } public SnapHashMap(final Map<? extends K, ? extends V> source) { this.rootHolder = new COWMgr<K,V>(); putAll(source); } @SuppressWarnings("unchecked") public SnapHashMap(final SortedMap<K,? extends V> source) { if (source instanceof SnapHashMap) { final SnapHashMap<K,V> s = (SnapHashMap<K,V>) source; this.rootHolder = (COWMgr<K,V>) s.rootHolder.clone(); } else { this.rootHolder = new COWMgr<K,V>(); putAll(source); } } @Override @SuppressWarnings("unchecked") public SnapHashMap<K,V> clone() { final SnapHashMap<K,V> copy; try { copy = (SnapHashMap<K,V>) super.clone(); } catch (final CloneNotSupportedException xx) { throw new InternalError(); } copy.rootHolder = (COWMgr<K,V>) rootHolder.clone(); return copy; } //////// public interface public void clear() { rootHolder = new COWMgr<K,V>(); } public boolean isEmpty() { return rootHolder.isEmpty(); } public int size() { return rootHolder.size(); } public boolean containsKey(final Object key) { return rootHolder.read().containsKey(key, hash(key.hashCode())); } public boolean containsValue(final Object value) { if (value == null) { throw new NullPointerException(); } return rootHolder.frozen().containsValueQ(value); } @SuppressWarnings("unchecked") public V get(final Object key) { //return rootHolder.read().get(key, hash(key.hashCode())); final int hash = hash(key.hashCode()); BranchMap<K,V> node = rootHolder.read(); while (true) { final Object child = node.get(node.indexFor(hash)); if (child instanceof LeafMap) { return ((LeafMap<K,V>) child).get(key, hash); } else if (child == null) { return null; } // else recurse node = (BranchMap<K,V>) child; } } public V put(final K key, final V value) { if (value == null) { throw new NullPointerException(); } final int h = hash(key.hashCode()); final Epoch.Ticket ticket = rootHolder.beginMutation(); int sizeDelta = 0; try { final V prev = rootHolder.mutable().put(key, h, value); if (prev == null) { sizeDelta = 1; } return prev; } finally { ticket.leave(sizeDelta); } } public V remove(final Object key) { final int h = hash(key.hashCode()); final Epoch.Ticket ticket = rootHolder.beginMutation(); int sizeDelta = 0; try { final V prev = rootHolder.mutable().remove(key, h); if (prev != null) { sizeDelta = -1; } return prev; } finally { ticket.leave(sizeDelta); } } //////// CAS-like public V putIfAbsent(final K key, final V value) { if (value == null) { throw new NullPointerException(); } final int h = hash(key.hashCode()); final Epoch.Ticket ticket = rootHolder.beginMutation(); int sizeDelta = 0; try { final V prev = rootHolder.mutable().putIfAbsent(key, h, value); if (prev == null) { sizeDelta = 1; } return prev; } finally { ticket.leave(sizeDelta); } } public boolean replace(final K key, final V oldValue, final V newValue) { if (oldValue == null || newValue == null) { throw new NullPointerException(); } final int h = hash(key.hashCode()); final Epoch.Ticket ticket = rootHolder.beginMutation(); try { return rootHolder.mutable().replace(key, h, oldValue, newValue); } finally { ticket.leave(0); } } public V replace(final K key, final V value) { if (value == null) { throw new NullPointerException(); } final int h = hash(key.hashCode()); final Epoch.Ticket ticket = rootHolder.beginMutation(); try { return rootHolder.mutable().replace(key, h, value); } finally { ticket.leave(0); } } public boolean remove(final Object key, final Object value) { final int h = hash(key.hashCode()); if (value == null) { return false; } final Epoch.Ticket ticket = rootHolder.beginMutation(); int sizeDelta = 0; try { final boolean result = rootHolder.mutable().remove(key, h, value); if (result) { sizeDelta = -1; } return result; } finally { ticket.leave(sizeDelta); } } public Set<K> keySet() { return new KeySet(); } public Collection<V> values() { return new Values(); } public Set<Entry<K,V>> entrySet() { return new EntrySet(); } //////// Legacy methods public boolean contains(final Object value) { return containsValue(value); } public Enumeration<K> keys() { return new KeyIterator(rootHolder.frozen()); } public Enumeration<V> elements() { return new ValueIterator(rootHolder.frozen()); } //////// Map support classes class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return new KeyIterator(rootHolder.frozen()); } public boolean isEmpty() { return SnapHashMap.this.isEmpty(); } public int size() { return SnapHashMap.this.size(); } public boolean contains(Object o) { return SnapHashMap.this.containsKey(o); } public boolean remove(Object o) { return SnapHashMap.this.remove(o) != null; } public void clear() { SnapHashMap.this.clear(); } } final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return new ValueIterator(rootHolder.frozen()); } public boolean isEmpty() { return SnapHashMap.this.isEmpty(); } public int size() { return SnapHashMap.this.size(); } public boolean contains(Object o) { return SnapHashMap.this.containsValue(o); } public void clear() { SnapHashMap.this.clear(); } } final class EntrySet extends AbstractSet<Entry<K,V>> { public Iterator<Entry<K,V>> iterator() { return new EntryIterator(rootHolder.frozen()); } public boolean contains(Object o) { if (!(o instanceof Entry)) return false; Entry<?,?> e = (Entry<?,?>)o; V v = SnapHashMap.this.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Entry)) return false; Entry<?,?> e = (Entry<?,?>)o; return SnapHashMap.this.remove(e.getKey(), e.getValue()); } public boolean isEmpty() { return SnapHashMap.this.isEmpty(); } public int size() { return SnapHashMap.this.size(); } public void clear() { SnapHashMap.this.clear(); } } abstract class AbstractIter { private final BranchMap<K,V> root; private LeafMap<K,V> currentLeaf; private HashEntry<K,V> currentEntry; private HashEntry<K,V> prevEntry; AbstractIter(final BranchMap<K,V> frozenRoot) { this.root = frozenRoot; findFirst(frozenRoot, 0); } @SuppressWarnings("unchecked") private boolean findFirst(final BranchMap<K,V> branch, final int minIndex) { for (int i = minIndex; i < BF; ++i) { final Object child = branch.get(i); if (child != null) { if (child instanceof LeafMap) { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; if (leaf.uniq > 0) { // success! for (HashEntry<K,V> e : leaf.table) { if (e != null) { currentLeaf = leaf; currentEntry = e; return true; } } throw new Error("logic error"); } } else { if (findFirst((BranchMap<K,V>) child, 0)) { return true; } } } } return false; } private void advance() { // advance within a bucket chain if (currentEntry.next != null) { // easy currentEntry = currentEntry.next; return; } // advance to the next non-empty chain int i = ((currentEntry.hash >> currentLeaf.shift) & (currentLeaf.table.length - 1)) + 1; while (i < currentLeaf.table.length) { if (currentLeaf.table[i] != null) { currentEntry = currentLeaf.table[i]; return; } ++i; } // now we are moving between LeafMap-s if (!findSuccessor(root)) { currentEntry = null; } } @SuppressWarnings("unchecked") private boolean findSuccessor(final BranchMap<K,V> branch) { final int h = currentEntry.hash; final int i = (h >> branch.shift) & BF_MASK; final Object child = branch.get(i); return (child instanceof BranchMap && findSuccessor((BranchMap<K,V>) child)) || findFirst(branch, i + 1); } public boolean hasNext() { return currentEntry != null; } public boolean hasMoreElements() { return hasNext(); } HashEntry<K,V> nextEntry() { if (currentEntry == null) { throw new NoSuchElementException(); } prevEntry = currentEntry; advance(); return prevEntry; } public void remove() { if (prevEntry == null) { throw new IllegalStateException(); } SnapHashMap.this.remove(prevEntry.key); prevEntry = null; } } final class KeyIterator extends AbstractIter implements Iterator<K>, Enumeration<K> { KeyIterator(final BranchMap<K, V> frozenRoot) { super(frozenRoot); } public K next() { return nextEntry().key; } public K nextElement() { return next(); } } final class ValueIterator extends AbstractIter implements Iterator<V>, Enumeration<V> { ValueIterator(final BranchMap<K, V> frozenRoot) { super(frozenRoot); } public V next() { return nextEntry().value; } public V nextElement() { return next(); } } final class WriteThroughEntry extends SimpleEntry<K,V> { WriteThroughEntry(final K k, final V v) { super(k, v); } public V setValue(final V value) { if (value == null) { throw new NullPointerException(); } final V prev = super.setValue(value); SnapHashMap.this.put(getKey(), value); return prev; } } final class EntryIterator extends AbstractIter implements Iterator<Entry<K,V>> { EntryIterator(final BranchMap<K, V> frozenRoot) { super(frozenRoot); } public Entry<K,V> next() { final HashEntry<K,V> e = nextEntry(); return new WriteThroughEntry(e.key, e.value); } } //////// Serialization /** Saves the state of the <code>SnapTreeMap</code> to a stream. */ private void writeObject(final ObjectOutputStream xo) throws IOException { // this handles the comparator, and any subclass stuff xo.defaultWriteObject(); // by cloning the COWMgr, we get a frozen tree plus the size final COWMgr<K,V> h = (COWMgr<K,V>) rootHolder.clone(); xo.writeInt(h.size()); writeEntry(xo, h.frozen()); } @SuppressWarnings("unchecked") private void writeEntry(final ObjectOutputStream xo, final BranchMap<K,V> branch) throws IOException { for (int i = 0; i < BF; ++i) { final Object child = branch.get(i); if (child != null) { if (child instanceof BranchMap) { writeEntry(xo, (BranchMap<K,V>) child); } else { final LeafMap<K,V> leaf = (LeafMap<K,V>) child; for (HashEntry<K,V> head : leaf.table) { HashEntry<K,V> e = head; while (e != null) { xo.writeObject(e.key); xo.writeObject(e.value); e = e.next; } } } } } } /** Reverses {@link #writeObject(java.io.ObjectOutputStream)}. */ @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream xi) throws IOException, ClassNotFoundException { xi.defaultReadObject(); final int size = xi.readInt(); final BranchMap<K,V> root = new BranchMap<K,V>(new Generation(), ROOT_SHIFT); for (int i = 0; i < size; ++i) { final K k = (K) xi.readObject(); final V v = (V) xi.readObject(); root.put(k, hash(k.hashCode()), v); } rootHolder = new COWMgr<K,V>(root, size); } // public static void main(final String[] args) { // for (int i = 0; i < 10; ++i) { // runOne(new SnapHashMap<Integer,String>()); // runOne(new SnapHashMap<Integer,String>()); // runOne(new SnapHashMap<Integer,String>()); // System.out.println(); // runOne(new java.util.concurrent.ConcurrentHashMap<Integer,String>()); // runOne(new java.util.concurrent.ConcurrentHashMap<Integer,String>()); // runOne(new java.util.concurrent.ConcurrentHashMap<Integer,String>()); // System.out.println(); //// runOne(new SnapTreeMap<Integer,String>()); //// runOne(new java.util.concurrent.ConcurrentSkipListMap<Integer,String>()); // System.out.println(); // } // } // // private static void runOne(final Map<Integer,String> m) { // final long t0 = System.currentTimeMillis(); // for (int p = 0; p < 10; ++p) { // for (int i = 0; i < 100000; ++i) { // m.put(Integer.reverse(i), "data"); // } // } // final long t1 = System.currentTimeMillis(); // for (int p = 0; p < 10; ++p) { // for (int i = 0; i < 100000; ++i) { // m.get(Integer.reverse(i)); // } // } // final long t2 = System.currentTimeMillis(); // for (int p = 0; p < 10; ++p) { // for (int i = 0; i < 100000; ++i) { // m.get(Integer.reverse(-(i + 1))); // } // } // final long t3 = System.currentTimeMillis(); // System.out.println( // (t1 - t0) + " nanos/put, " + // (t2 - t1) + " nanos/get hit, " + // (t3 - t2) + " nanos/get miss : " + m.getClass().getSimpleName()); // } }