diff options
author | Christophe Grand <christophe@cgrand.net> | 2009-08-08 10:40:29 +0200 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-08-26 12:02:56 -0400 |
commit | cf35b137bc41cf382ff990f218083a18f36815d9 (patch) | |
tree | 338d7e0f1801436fb79f08422437bd79829f0b31 | |
parent | 14316ae2110a779ffc8ac9c3da3f1c41852c4289 (diff) |
first cut at a leafless, not always packed PersistentHashMap
Signed-off-by: Rich Hickey <richhickey@gmail.com>
-rw-r--r-- | src/jvm/clojure/lang/PersistentHashMap2.java | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/src/jvm/clojure/lang/PersistentHashMap2.java b/src/jvm/clojure/lang/PersistentHashMap2.java new file mode 100644 index 00000000..0471c56e --- /dev/null +++ b/src/jvm/clojure/lang/PersistentHashMap2.java @@ -0,0 +1,919 @@ +/** + Copyright (c) 2007-2008, Rich Hickey + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Clojure nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + **/ + +/* rich Jun 18, 2007 */ + +package clojure.lang; + +import java.util.*; +//this stuff is just for the test main() +import java.util.concurrent.atomic.AtomicReference; + +import clojure.lang.PersistentVector.Node; + +/* + A persistent rendition of Phil Bagwell's Hash Array Mapped Trie + + Uses path copying for persistence + HashCollision leaves vs. extended hashing + Node polymorphism vs. conditionals + No sub-tree pools or root-resizing + Any errors are my own + */ + +public class PersistentHashMap2 extends APersistentMap /* implements IEditableCollection */ { + +final int count; +final INode root; +final boolean hasNull; +final Object nullValue; + +final public static PersistentHashMap2 EMPTY = new PersistentHashMap2(0, null, false, null); +final private static Object NOT_FOUND = new Object(); + +static public IPersistentMap create(Map other){ + IPersistentMap ret = EMPTY; + for(Object o : other.entrySet()) + { + Map.Entry e = (Entry) o; + ret = ret.assoc(e.getKey(), e.getValue()); + } + return ret; +} + +/* + * @param init {key1,val1,key2,val2,...} + */ +public static PersistentHashMap2 create(Object... init){ + IPersistentMap ret = EMPTY; + for(int i = 0; i < init.length; i += 2) + { + ret = ret.assoc(init[i], init[i + 1]); + } + return (PersistentHashMap2) ret; +} + +public static PersistentHashMap2 create(List init){ + IPersistentMap ret = EMPTY; + for(Iterator i = init.iterator(); i.hasNext();) + { + Object key = i.next(); + if(!i.hasNext()) + throw new IllegalArgumentException(String.format("No value supplied for key: %s", key)); + Object val = i.next(); + ret = ret.assoc(key, val); + } + return (PersistentHashMap2) ret; +} + +static public PersistentHashMap2 create(ISeq items){ + IPersistentMap ret = EMPTY; + for(; items != null; items = items.next().next()) + { + if(items.next() == null) + throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first())); + ret = ret.assoc(items.first(), RT.second(items)); + } + return (PersistentHashMap2) ret; +} + +/* + * @param init {key1,val1,key2,val2,...} + */ +public static PersistentHashMap2 create(IPersistentMap meta, Object... init){ + IPersistentMap ret = EMPTY.withMeta(meta); + for(int i = 0; i < init.length; i += 2) + { + ret = ret.assoc(init[i], init[i + 1]); + } + return (PersistentHashMap2) ret; +} + +PersistentHashMap2(int count, INode root, boolean hasNull, Object nullValue){ + this.count = count; + this.root = root; + this.hasNull = hasNull; + this.nullValue = nullValue; +} + +public PersistentHashMap2(IPersistentMap meta, int count, INode root, boolean hasNull, Object nullValue){ + super(meta); + this.count = count; + this.root = root; + this.hasNull = hasNull; + this.nullValue = nullValue; +} + +public boolean containsKey(Object key){ + if(key == null) + return hasNull; + return (root != null) ? root.find(0, Util.hash(key), key, NOT_FOUND) != NOT_FOUND : false; +} + +public IMapEntry entryAt(Object key){ + if(key == null) + return hasNull ? new MapEntry(null, nullValue) : null; + return (root != null) ? root.find(0, Util.hash(key), key) : null; +} + +public IPersistentMap assoc(Object key, Object val){ + if(key == null) { + if(hasNull && val == nullValue) + return this; + return new PersistentHashMap2(meta(), hasNull ? count : count + 1, root, true, val); + } + Box addedLeaf = new Box(null); + INode newroot = (root == null ? BitmapIndexedNode.EMPTY : root) + .assoc(0, Util.hash(key), key, val, addedLeaf); + if(newroot == root) + return this; + return new PersistentHashMap2(meta(), addedLeaf.val == null ? count : count + 1, newroot, hasNull, nullValue); +} + +public Object valAt(Object key, Object notFound){ + if(key == null) + return hasNull ? nullValue : notFound; + return root != null ? root.find(0, Util.hash(key), key, notFound) : notFound; +} + +public Object valAt(Object key){ + return valAt(key, null); +} + +public IPersistentMap assocEx(Object key, Object val) throws Exception{ + if(containsKey(key)) + throw new Exception("Key already present"); + return assoc(key, val); +} + +public IPersistentMap without(Object key){ + if(key == null) + return hasNull ? new PersistentHashMap2(meta(), count - 1, root, false, null) : this; + if(root == null) + return this; + INode newroot = root.without(0, Util.hash(key), key); + if(newroot == root) + return this; + return new PersistentHashMap2(meta(), count - 1, newroot, hasNull, nullValue); +} + +public Iterator iterator(){ + return new SeqIterator(seq()); +} + +public int count(){ + return count; +} + +public ISeq seq(){ + ISeq s = root != null ? root.nodeSeq() : null; + return hasNull ? new Cons(new MapEntry(null, nullValue), s) : s; +} + +public IPersistentCollection empty(){ + return EMPTY.withMeta(meta()); +} + +static int mask(int hash, int shift){ + //return ((hash << shift) >>> 27);// & 0x01f; + return (hash >>> shift) & 0x01f; +} + +public PersistentHashMap2 withMeta(IPersistentMap meta){ + return new PersistentHashMap2(meta, count, root, hasNull, nullValue); +} + +public TransientHashMap asTransient() { + return new TransientHashMap(this); +} + +static final class TransientHashMap extends ATransientMap { + AtomicReference<Thread> edit; + INode root; + int count; + + TransientHashMap(PersistentHashMap2 m) { + this(new AtomicReference<Thread>(Thread.currentThread()), m.root, m.count); + } + + TransientHashMap(AtomicReference<Thread> edit, INode root, int count) { + this.edit = edit; + this.root = root; + this.count = count; + } + + ITransientMap doAssoc(Object key, Object val) { + Box addedLeaf = new Box(null); + this.root = root.assoc(edit, 0, Util.hash(key), key, val, addedLeaf); + if(addedLeaf.val != null) this.count++; + return this; + } + + ITransientMap doWithout(Object key) { + // TODO +// Box removedLeaf = new Box(null); +// INode newroot = root.without(edit, null, Util.hash(key), key, removedLeaf); +// this.root = newroot == null ? EMPTY.root : newroot; +// if(removedLeaf.val != null) this.count--; + return this; + } + + IPersistentMap doPersistent() { + edit.set(null); + return null; // TODO new PersistentHashMap(count, root); + } + + private IMapEntry entryAt(Object key){ + // TODO + // return root.find(null, null, Util.hash(key), key); + return null; + } + + Object doValAt(Object key, Object notFound) { + IMapEntry e = entryAt(key); + if(e != null) + return e.val(); + return notFound; + } + + int doCount() { + return count; + } + + void ensureEditable(){ + Thread owner = edit.get(); + if(owner == Thread.currentThread()) + return; + if(owner != null) + throw new IllegalAccessError("Mutable used by non-owner thread"); + throw new IllegalAccessError("Mutable used after immutable call"); + } +} + +/* +final static int[] pathmasks = new int[32]; +static{ + pathmasks[0] = 0; + for(int i=1;i<32;i++) + pathmasks[i] = 0x80000000 >> (i - 1); +} +//final static int pathmask(int hash, int shift){ +// //return hash & (0x80000000 >> (shift - 1)); +// return hash & pathmasks[shift]; +// } + +static boolean diffPath(int shift, int hash1, int hash2){ + return shift > 0 && ((hash1^hash2) & pathmasks[shift]) != 0; + //return shift > 0 && pathmask(hash1^hash2,shift) != 0; +} +*/ +static interface INode{ + INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf); + + INode without(int shift, int hash, Object key); + + IMapEntry find(int shift, int hash, Object key); + + Object find(int shift, int hash, Object key, Object notFound); + + ISeq nodeSeq(); + + INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf); + + INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf); +} + +final static class ArrayNode implements INode{ + final Object[] array; + final AtomicReference<Thread> edit; + + ArrayNode(AtomicReference<Thread> edit, Object[] array){ + this.array = array; + this.edit = edit; + } + + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ + int idx = mask(hash, shift); + + Object keyOrNode = array[2*idx]; + if(keyOrNode == null) + return new ArrayNode(null, cloneAndSet(array, 2*idx, key, 2*idx+1, val)); + if(keyOrNode instanceof INode) { + INode n = ((INode) keyOrNode).assoc(shift + 5, hash, key, val, addedLeaf); + if(n == keyOrNode) + return this; + return new ArrayNode(null, cloneAndSet(array, 2*idx, n)); + } + if(Util.equals(key, keyOrNode)) { + if(val == array[2*idx+1]) + return this; + return new ArrayNode(null, cloneAndSet(array, 2*idx+1, val)); + } + return new ArrayNode(null, cloneAndSet(array, + 2*idx, createNode(shift + 5, keyOrNode, array[2*idx+1], hash, key, val), + 2*idx+1, null)); + } + + public INode without(int shift, int hash, Object key){ + int idx = mask(hash, shift); + Object keyOrNode = array[2*idx]; + if(keyOrNode == null) + return this; + if(keyOrNode instanceof INode) { + INode n = ((INode) keyOrNode).without(shift + 5, hash, key); + if(n == keyOrNode) + return this; + // TODO: shrink if null + return new ArrayNode(null, cloneAndSet(array, 2*idx, n)); + } + if(Util.equals(key, keyOrNode)) + // TODO: shrink + return new ArrayNode(null, cloneAndSet(array, 2*idx, null, 2*idx+1, null)); + return this; + } + + public IMapEntry find(int shift, int hash, Object key){ + int idx = mask(hash, shift); + Object keyOrNode = array[2*idx]; + if(keyOrNode == null) + return null; + return deepFind(shift, hash, keyOrNode, array[2*idx+1], key); + } + + public Object find(int shift, int hash, Object key, Object notFound){ + int idx = mask(hash, shift); + Object keyOrNode = array[2*idx]; + if(keyOrNode == null) + return notFound; + return deepFind(shift, hash, keyOrNode, array[2*idx+1], key, notFound); + } + + public ISeq nodeSeq(){ + return NodeSeq.create(array); + } + + ArrayNode ensureEditable(AtomicReference<Thread> edit){ +// TODO +// if(this.edit == edit) + return this; +// return new ArrayNode(edit, this.nodes.clone()); + } + + public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){ + return null; + // TODO +// int idx = mask(hash, shift); +// +// INode n = nodes[idx].assoc(edit, shift + 5, hash, key, val, addedLeaf); +// if(n == nodes[idx]) +// return this; +// else +// { +// ArrayNode node = this.ensureEditable(edit); +// node.nodes[idx] = n; +// return node; +// } + } + + public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){ + return null; +// int idx = mask(hash, shift); +// INode n = nodes[idx].without(edit, null, hash, key, removedLeaf); +// if(n != nodes[idx]) +// { +// if(n == null) +// { +// INode[] newnodes = new INode[nodes.length - 1]; +// System.arraycopy(nodes, 0, newnodes, 0, idx); +// System.arraycopy(nodes, idx + 1, newnodes, idx, nodes.length - (idx + 1)); +// return new BitmapIndexedNode(edit, ~bitpos(hash, shift), newnodes, shift); +// } +// ArrayNode node = ensureEditable(edit); +// node.nodes[idx] = n; +// return node; +// } +// return this; + } +} + +final static class BitmapIndexedNode implements INode{ + static final BitmapIndexedNode EMPTY = new BitmapIndexedNode(null, 0, new Object[0]); + + final int bitmap; + final Object[] array; + final AtomicReference<Thread> edit; + + final int index(int bit){ + return Integer.bitCount(bitmap & (bit - 1)); + } + + BitmapIndexedNode(AtomicReference<Thread> edit, int bitmap, Object[] array){ + this.bitmap = bitmap; + this.array = array; + this.edit = edit; + } + + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ + int bit = bitpos(hash, shift); + int idx = index(bit); + if((bitmap & bit) != 0) { + Object keyOrNode = array[2*idx]; + if(keyOrNode instanceof INode) { + INode n = ((INode) keyOrNode).assoc(shift + 5, hash, key, val, addedLeaf); + if(n == keyOrNode) + return this; + return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx, n)); + } + if(Util.equals(key, keyOrNode)) { + if(val == array[2*idx+1]) + return this; + return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx+1, val)); + } + return new BitmapIndexedNode(null, bitmap, + cloneAndSet(array, + 2*idx, createNode(shift + 5, keyOrNode, array[2*idx+1], hash, key, val), + 2*idx+1, null)); + } else { + int n = Integer.bitCount(bitmap); + if(n >= 16) { + Object[] newArray = new Object[64]; + int jdx = mask(hash, shift); + newArray[2*jdx] = key; + newArray[2*jdx+1] = val; + int j = 0; + for(int i = 0; i < 32; i++) + if(((bitmap >>> i) & 1) != 0) { + newArray[2*i] = array[j]; + newArray[2*i+1] = array[j+1]; + j += 2; + } + return new ArrayNode(null, newArray); + } else { + int len = Integer.bitCount(bitmap); + Object[] newArray = new Object[2*(len+1)]; + System.arraycopy(array, 0, newArray, 0, 2*idx); + newArray[2*idx] = key; + addedLeaf.val = newArray[2*idx+1] = val; + System.arraycopy(array, 2*idx, newArray, 2*(idx+1), 2*(len-idx)); + return new BitmapIndexedNode(null, bitmap | bit, newArray); + } + } + } + + public INode without(int shift, int hash, Object key){ + int bit = bitpos(hash, shift); + if((bitmap & bit) == 0) + return this; + int idx = index(bit); + Object keyOrNode = array[2*idx]; + if(keyOrNode == null) + return this; + if(keyOrNode instanceof INode) { + INode n = ((INode) keyOrNode).without(shift + 5, hash, key); + if(n == keyOrNode) + return this; + if(n == null) { + if(array.length == 2) + return null; + // TODO: collapse + return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx)); + } + return new BitmapIndexedNode(null, bitmap, cloneAndSet(array, 2*idx, n)); + } + if(Util.equals(key, keyOrNode)) + // TODO: collapse + return new BitmapIndexedNode(null, bitmap ^ bit, removePair(array, idx)); + return this; + } + + public IMapEntry find(int shift, int hash, Object key){ + int bit = bitpos(hash, shift); + if((bitmap & bit) == 0) + return null; + int idx = index(bit); + return deepFind(shift, hash, array[2*idx], array[2*idx+1], key); + } + + public Object find(int shift, int hash, Object key, Object notFound){ + int bit = bitpos(hash, shift); + if((bitmap & bit) == 0) + return notFound; + int idx = index(bit); + return deepFind(shift, hash, array[2*idx], array[2*idx+1], key, notFound); + } + + public ISeq nodeSeq(){ + return NodeSeq.create(array); + } + + BitmapIndexedNode ensureEditable(AtomicReference<Thread> edit){ + return null; + // TODO +// if(this.edit == edit) +// return this; +// return new BitmapIndexedNode(edit, bitmap, nodes.clone(), shift); + } + + public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){ + return null; + // TODO +// int bit = bitpos(hash, shift); +// int idx = index(bit); +// if((bitmap & bit) != 0) +// { +// INode n = nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf); +// if(n == nodes[idx]) +// return this; +// else +// { +// BitmapIndexedNode node = ensureEditable(edit); +// node.nodes[idx] = n; +// return node; +// } +// } +// else +// { +// // TODO can do better +// INode[] newnodes = new INode[nodes.length + 1]; +// System.arraycopy(nodes, 0, newnodes, 0, idx); +// addedLeaf.val = newnodes[idx] = new IMapEntry(null, hash, key, val); +// System.arraycopy(nodes, idx, newnodes, idx + 1, nodes.length - idx); +// return create(edit, bitmap | bit, newnodes, shift); +// } + } + + public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){ + return null; + // TODO +// int bit = bitpos(hash, shift); +// if((bitmap & bit) != 0) +// { +// int idx = index(bit); +// INode n = nodes[idx].without(edit, null, hash, key, removedLeaf); +// if(n != nodes[idx]) +// { +// if(n == null) +// { +// if(bitmap == bit) +// return null; +// INode[] newnodes = new INode[nodes.length - 1]; +// System.arraycopy(nodes, 0, newnodes, 0, idx); +// System.arraycopy(nodes, idx + 1, newnodes, idx, nodes.length - (idx + 1)); +// return new BitmapIndexedNode(edit, bitmap & ~bit, newnodes, shift); +// } +// BitmapIndexedNode node = ensureEditable(edit); +// node.nodes[idx] = n; +// return node; +// } +// } +// return this; + } +} + +final static class HashCollisionNode implements INode{ + + final int hash; + final Object[] array; + final AtomicReference<Thread> edit; + + HashCollisionNode(AtomicReference<Thread> edit, int hash, Object... array){ + this.edit = edit; + this.hash = hash; + this.array = array; + } + + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ + if(hash == this.hash) { + int idx = findIndex(key); + if(idx != -1) { + if(array[idx + 1] == val) + return this; + return new HashCollisionNode(null, hash, cloneAndSet(array, idx + 1, val)); + } + Object[] newArray = new Object[array.length + 2]; + System.arraycopy(array, 0, newArray, 0, array.length); + newArray[array.length] = key; + newArray[array.length + 1] = val; + return new HashCollisionNode(null, hash, newArray); + } + // nest it in a bitmap node + return new BitmapIndexedNode(null, bitpos(this.hash, shift), new Object[] {this}) + .assoc(shift, hash, key, val, addedLeaf); + } + + public INode without(int shift, int hash, Object key){ + int idx = findIndex(key); + if(idx == -1) + return this; + if(array.length == 2) + return null; + return new HashCollisionNode(null, hash, removePair(array, idx)); + } + + public IMapEntry find(int shift, int hash, Object key){ + int idx = findIndex(key); + if(idx < 0) + return null; + if(Util.equals(key, array[idx])) + return new MapEntry(array[idx], array[idx+1]); + return null; + } + + public Object find(int shift, int hash, Object key, Object notFound){ + int idx = findIndex(key); + if(idx < 0) + return notFound; + if(Util.equals(key, array[idx])) + return array[idx+1]; + return notFound; + } + + public ISeq nodeSeq(){ + return NodeSeq.create(array); + } + + public int findIndex(Object key){ + for(int i = 0; i < array.length; i+=2) + { + if(Util.equals(key, array[i])) + return i; + } + return -1; + } + + HashCollisionNode ensureEditable(AtomicReference<Thread> edit){ + return null; + // TODO +// if(this.edit == edit) +// return this; +// return new HashCollisionNode(edit, hash, leaves); + } + + public INode assoc(AtomicReference<Thread> edit, int shift, int hash, Object key, Object val, Box addedLeaf){ + return null; + // TODO +// if(hash == this.hash) +// { +// int idx = findIndex(hash, key); +// if(idx != -1) +// { +// if(leaves[idx].val == val) +// return this; +// IMapEntry leaf = leaves[idx].ensureEditable(edit); +// leaf.val = val; +// if(leaves[idx] == leaf) +// return this; +// HashCollisionNode node = ensureEditable(edit); +// node.leaves[idx] = leaf; +// return node; +// } +// IMapEntry[] newLeaves = new IMapEntry[leaves.length + 1]; +// System.arraycopy(leaves, 0, newLeaves, 0, leaves.length); +// addedLeaf.val = newLeaves[leaves.length] = new IMapEntry(null, hash, key, val); +// return new HashCollisionNode(edit, hash, newLeaves); +// } +// return BitmapIndexedNode.create(edit, shift, this, hash, key, val, addedLeaf); + } + + public INode without(AtomicReference<Thread> edit, int shift, int hash, Object key, Box removedLeaf){ + return null; + // TODO +// int idx = findIndex(hash, key); +// if(idx == -1) +// return this; +// removedLeaf.val = leaves[idx]; +// if(leaves.length == 2) +// return idx == 0 ? leaves[1] : leaves[0]; +// IMapEntry[] newLeaves = new IMapEntry[leaves.length - 1]; +// System.arraycopy(leaves, 0, newLeaves, 0, idx); +// System.arraycopy(leaves, idx + 1, newLeaves, idx, leaves.length - (idx + 1)); +// return new HashCollisionNode(edit, hash, newLeaves); + } +} + +/* +public static void main(String[] args){ + try + { + ArrayList words = new ArrayList(); + Scanner s = new Scanner(new File(args[0])); + s.useDelimiter(Pattern.compile("\\W")); + while(s.hasNext()) + { + String word = s.next(); + words.add(word); + } + System.out.println("words: " + words.size()); + IPersistentMap map = PersistentHashMap.EMPTY; + //IPersistentMap map = new PersistentTreeMap(); + //Map ht = new Hashtable(); + Map ht = new HashMap(); + Random rand; + + System.out.println("Building map"); + long startTime = System.nanoTime(); + for(Object word5 : words) + { + map = map.assoc(word5, word5); + } + rand = new Random(42); + IPersistentMap snapshotMap = map; + for(int i = 0; i < words.size() / 200; i++) + { + map = map.without(words.get(rand.nextInt(words.size() / 2))); + } + long estimatedTime = System.nanoTime() - startTime; + System.out.println("count = " + map.count() + ", time: " + estimatedTime / 1000000); + + System.out.println("Building ht"); + startTime = System.nanoTime(); + for(Object word1 : words) + { + ht.put(word1, word1); + } + rand = new Random(42); + for(int i = 0; i < words.size() / 200; i++) + { + ht.remove(words.get(rand.nextInt(words.size() / 2))); + } + estimatedTime = System.nanoTime() - startTime; + System.out.println("count = " + ht.size() + ", time: " + estimatedTime / 1000000); + + System.out.println("map lookup"); + startTime = System.nanoTime(); + int c = 0; + for(Object word2 : words) + { + if(!map.contains(word2)) + ++c; + } + estimatedTime = System.nanoTime() - startTime; + System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000); + System.out.println("ht lookup"); + startTime = System.nanoTime(); + c = 0; + for(Object word3 : words) + { + if(!ht.containsKey(word3)) + ++c; + } + estimatedTime = System.nanoTime() - startTime; + System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000); + System.out.println("snapshotMap lookup"); + startTime = System.nanoTime(); + c = 0; + for(Object word4 : words) + { + if(!snapshotMap.contains(word4)) + ++c; + } + estimatedTime = System.nanoTime() - startTime; + System.out.println("notfound = " + c + ", time: " + estimatedTime / 1000000); + } + catch(FileNotFoundException e) + { + e.printStackTrace(); + } + +} +*/ + +private static Object[] cloneAndSet(Object[] array, int i, Object a) { + Object[] clone = array.clone(); + clone[i] = a; + return clone; +} + +private static Object[] cloneAndSet(Object[] array, int i, Object a, int j, Object b) { + Object[] clone = array.clone(); + clone[i] = a; + clone[j] = b; + return clone; +} + +private static Object[] removePair(Object[] array, int i) { + Object[] newArray = new Object[array.length - 2]; + System.arraycopy(array, 0, newArray, 0, 2*i); + System.arraycopy(array, 2*(i+1), newArray, 2*i, newArray.length - 2*i); + return newArray; +} + +private static INode createNode(int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) { + int key1hash = Util.hash(key1); + if(key1hash == key2hash) + return new HashCollisionNode(null, key1hash, new Object[] {key1, val1, key2, val2}); + // TODO: optimize; + Box _ = new Box(null); + return BitmapIndexedNode.EMPTY + .assoc(shift, key1hash, key1, val1, _) + .assoc(shift, key2hash, key2, val2, _); +} + +private static Object deepFind(int shift, int hash, Object keyOrNode, Object maybeVal, Object key, Object notFound) { + if(keyOrNode == null) + return notFound; + if(keyOrNode instanceof INode) + return ((INode) keyOrNode).find(shift + 5, hash, key, notFound); + if(Util.equals(key, keyOrNode)) + return maybeVal; + return notFound; +} + +private static IMapEntry deepFind(int shift, int hash, Object keyOrNode, Object maybeVal, Object key) { + if(keyOrNode == null) + return null; + if(keyOrNode instanceof INode) + return ((INode) keyOrNode).find(shift + 5, hash, key); + if(Util.equals(key, keyOrNode)) + return new MapEntry(keyOrNode, maybeVal); + return null; +} + +private static int bitpos(int hash, int shift){ + return 1 << mask(hash, shift); +} + +static final class NodeSeq extends ASeq { + final Object[] array; + final int i; + final ISeq s; + + NodeSeq(Object[] array, int i) { + this(null, array, i, null); + } + + static ISeq create(Object[] array) { + return create(array, 0, null); + } + + private static ISeq create(Object[] array, int i, ISeq s) { + if(s != null) + return new NodeSeq(null, array, i, s); + for(int j = i; j < array.length; j+=2) { + Object keyOrNode = array[j]; + if(keyOrNode instanceof INode) { + ISeq nodeSeq = ((INode) keyOrNode).nodeSeq(); + if(nodeSeq != null) + return new NodeSeq(null, array, j + 2, nodeSeq); + } else if(keyOrNode != null) + return new NodeSeq(null, array, j, null); + } + return null; + } + + NodeSeq(IPersistentMap meta, Object[] array, int i, ISeq s) { + super(meta); + this.array = array; + this.i = i; + this.s = s; + } + + public Obj withMeta(IPersistentMap meta) { + return new NodeSeq(meta, array, i, s); + } + + public Object first() { + if(s != null) + return s.first(); + return new MapEntry(array[i], array[i+1]); + } + + public ISeq next() { + if(s != null) + return create(array, i, s.next()); + return create(array, i + 2, null); + } +} + +}
\ No newline at end of file |