diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/jvm/clojure/lang/PersistentHashMap.java | 211 |
1 files changed, 165 insertions, 46 deletions
diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java index da3c17e3..1b6f23f9 100644 --- a/src/jvm/clojure/lang/PersistentHashMap.java +++ b/src/jvm/clojure/lang/PersistentHashMap.java @@ -49,10 +49,11 @@ public IMapEntry find(Object key){ } public IPersistentMap assoc(Object key, Object val){ - INode newroot = root.assoc(0, RT.hash(key), key, val); + Box addedLeaf = new Box(null); + INode newroot = root.assoc(0, RT.hash(key), key, val, addedLeaf); if(newroot == root) return this; - PersistentHashMap ret = new PersistentHashMap(count + 1, newroot); + PersistentHashMap ret = new PersistentHashMap(addedLeaf.val == null ? count : count + 1, newroot); ret._meta = this._meta; return ret; } @@ -94,11 +95,11 @@ public ISeq seq(){ } static interface INode{ - INode assoc(int shift, int hash, Object key, Object val); + INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf); INode without(int hash, Object key); - Leaf find(int hash, Object key); + LeafNode find(int hash, Object key); ISeq seq(); } @@ -109,15 +110,17 @@ static interface ILeaf extends INode{ final static class EmptyNode implements INode{ - public INode assoc(int shift, int hash, Object key, Object val){ - return new Leaf(hash, key, val); + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ + INode ret = new LeafNode(hash, key, val); + addedLeaf.val = ret; + return ret; } public INode without(int hash, Object key){ return this; } - public Leaf find(int hash, Object key){ + public LeafNode find(int hash, Object key){ return null; } @@ -126,13 +129,109 @@ final static class EmptyNode implements INode{ } } -final static class Node implements INode{ +final static class FullNode implements INode{ + final INode[] nodes; + final int shift; + + static int mask(int hash, int shift){ + return (hash >>> shift) & 0x01f; + } + + static int bitpos(int hash, int shift){ + return 1 << mask(hash, shift); + } + + FullNode(INode[] nodes, int shift){ + this.nodes = nodes; + this.shift = shift; + } + + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ + int idx = mask(hash, shift); + + INode n = nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf); + if(n == nodes[idx]) + return this; + else + { + INode[] newnodes = nodes.clone(); + newnodes[idx] = n; + return new FullNode(newnodes, shift); + } + } + + public INode without(int hash, Object key){ + + int idx = mask(hash, shift); + INode n = nodes[idx].without(hash, key); + 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(~bitpos(hash, shift), newnodes, shift); + } + INode[] newnodes = nodes.clone(); + newnodes[idx] = n; + return new FullNode(newnodes, shift); + } + return this; + } + + public LeafNode find(int hash, Object key){ + return nodes[mask(hash, shift)].find(hash, key); + } + + public ISeq seq(){ + return Seq.create(this, 0); + } + + static class Seq extends ASeq{ + final ISeq s; + final int i; + final FullNode node; + + + Seq(ISeq s, int i, FullNode node){ + this.s = s; + this.i = i; + this.node = node; + } + + static ISeq create(FullNode node, int i){ + if(i >= node.nodes.length) + return null; + return new Seq(node.nodes[i].seq(), i, node); + } + + public Object first(){ + return s.first(); + } + + public ISeq rest(){ + ISeq nexts = s.rest(); + if(nexts != null) + return new Seq(nexts, i, node); + return create(node, i + 1); + } + } + + +} + +final static class BitmapIndexedNode implements INode{ final int bitmap; final INode[] nodes; final int shift; static int mask(int hash, int shift){ - return 1 << ((hash >>> shift) & 0x01f); + return (hash >>> shift) & 0x01f; + } + + static int bitpos(int hash, int shift){ + return 1 << mask(hash, shift); } final int index(int bit){ @@ -140,44 +239,50 @@ final static class Node implements INode{ } - Node(int bitmap, INode[] nodes, int shift){ + BitmapIndexedNode(int bitmap, INode[] nodes, int shift){ this.bitmap = bitmap; this.nodes = nodes; this.shift = shift; } - static INode create(int shift, ILeaf leaf, int hash, Object key, Object val){ - return (new Node(mask(leaf.getHash(), shift), new INode[]{leaf}, shift)) - .assoc(shift, hash, key, val); + static INode create(int bitmap, INode[] nodes, int shift){ + if(bitmap == -1) + return new FullNode(nodes, shift); + return new BitmapIndexedNode(bitmap, nodes, shift); + } + + static INode create(int shift, ILeaf leaf, int hash, Object key, Object val, Box addedLeaf){ + return (new BitmapIndexedNode(bitpos(leaf.getHash(), shift), new INode[]{leaf}, shift)) + .assoc(shift, hash, key, val, addedLeaf); } - public INode assoc(int shift, int hash, Object key, Object val){ - int bit = mask(hash, shift); + 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) { - INode n = nodes[idx].assoc(shift + 5, hash, key, val); + INode n = nodes[idx].assoc(shift + 5, hash, key, val, addedLeaf); if(n == nodes[idx]) return this; else { INode[] newnodes = nodes.clone(); newnodes[idx] = n; - return new Node(bitmap, newnodes, shift); + return new BitmapIndexedNode(bitmap, newnodes, shift); } } else { INode[] newnodes = new INode[nodes.length + 1]; System.arraycopy(nodes, 0, newnodes, 0, idx); - newnodes[idx] = new Leaf(hash, key, val); + addedLeaf.val = newnodes[idx] = new LeafNode(hash, key, val); System.arraycopy(nodes, idx, newnodes, idx + 1, nodes.length - idx); - return new Node(bitmap | bit, newnodes, shift); + return create(bitmap | bit, newnodes, shift); } } public INode without(int hash, Object key){ - int bit = mask(hash, shift); + int bit = bitpos(hash, shift); if((bitmap & bit) != 0) { int idx = index(bit); @@ -191,18 +296,18 @@ final static class Node implements INode{ 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 Node(bitmap & ~bit, newnodes, shift); + return new BitmapIndexedNode(bitmap & ~bit, newnodes, shift); } INode[] newnodes = nodes.clone(); newnodes[idx] = n; - return new Node(bitmap, newnodes, shift); + return new BitmapIndexedNode(bitmap, newnodes, shift); } } return this; } - public Leaf find(int hash, Object key){ - int bit = mask(hash, shift); + public LeafNode find(int hash, Object key){ + int bit = bitpos(hash, shift); if((bitmap & bit) != 0) { return nodes[index(bit)].find(hash, key); @@ -218,16 +323,16 @@ final static class Node implements INode{ static class Seq extends ASeq{ final ISeq s; final int i; - final Node node; + final BitmapIndexedNode node; - Seq(ISeq s, int i, Node node){ + Seq(ISeq s, int i, BitmapIndexedNode node){ this.s = s; this.i = i; this.node = node; } - static ISeq create(Node node, int i){ + static ISeq create(BitmapIndexedNode node, int i){ if(i >= node.nodes.length) return null; return new Seq(node.nodes[i].seq(), i, node); @@ -248,26 +353,33 @@ final static class Node implements INode{ } -final static class Leaf implements ILeaf, IMapEntry{ +final static class LeafNode implements ILeaf, IMapEntry{ final int hash; final Object key; final Object val; - public Leaf(int hash, Object key, Object val){ + public LeafNode(int hash, Object key, Object val){ this.hash = hash; this.key = key; this.val = val; } - public INode assoc(int shift, int hash, Object key, Object val){ + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ if(hash == this.hash) { if(RT.equal(key, this.key)) - return this; + { + if(RT.equal(val, this.val)) + return this; + //note - do not set addedLeaf, since we are replacing + return new LeafNode(hash, key, val); + } //hash collision - same hash, different keys - return new HashCollisionLeaf(hash, this, new Leaf(hash, key, val)); + LeafNode newLeaf = new LeafNode(hash, key, val); + addedLeaf.val = newLeaf; + return new HashCollisionNode(hash, this, newLeaf); } - return Node.create(shift, this, hash, key, val); + return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf); } public INode without(int hash, Object key){ @@ -276,7 +388,7 @@ final static class Leaf implements ILeaf, IMapEntry{ return this; } - public Leaf find(int hash, Object key){ + public LeafNode find(int hash, Object key){ if(hash == this.hash && RT.equal(key, this.key)) return this; return null; @@ -300,28 +412,35 @@ final static class Leaf implements ILeaf, IMapEntry{ } -final static class HashCollisionLeaf implements ILeaf{ +final static class HashCollisionNode implements ILeaf{ final int hash; - final Leaf[] leaves; + final LeafNode[] leaves; - public HashCollisionLeaf(int hash, Leaf... leaves){ + public HashCollisionNode(int hash, LeafNode... leaves){ this.hash = hash; this.leaves = leaves; } - public INode assoc(int shift, int hash, Object key, Object val){ + public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ if(hash == this.hash) { int idx = findIndex(hash, key); if(idx != -1) - return this; - Leaf[] newLeaves = new Leaf[leaves.length + 1]; + { + if(RT.equal(leaves[idx].val, val)) + return this; + LeafNode[] newLeaves = leaves.clone(); + //note - do not set addedLeaf, since we are replacing + newLeaves[idx] = new LeafNode(hash, key, val); + return new HashCollisionNode(hash, newLeaves); + } + LeafNode[] newLeaves = new LeafNode[leaves.length + 1]; System.arraycopy(leaves, 0, newLeaves, 0, leaves.length); - newLeaves[leaves.length] = new Leaf(hash, key, val); - return new HashCollisionLeaf(hash, newLeaves); + addedLeaf.val = newLeaves[leaves.length] = new LeafNode(hash, key, val); + return new HashCollisionNode(hash, newLeaves); } - return Node.create(shift, this, hash, key, val); + return BitmapIndexedNode.create(shift, this, hash, key, val, addedLeaf); } public INode without(int hash, Object key){ @@ -330,13 +449,13 @@ final static class HashCollisionLeaf implements ILeaf{ return this; if(leaves.length == 2) return idx == 0 ? leaves[1] : leaves[0]; - Leaf[] newLeaves = new Leaf[leaves.length - 1]; + LeafNode[] newLeaves = new LeafNode[leaves.length - 1]; System.arraycopy(leaves, 0, newLeaves, 0, idx); System.arraycopy(leaves, idx + 1, newLeaves, idx, leaves.length - (idx + 1)); - return new HashCollisionLeaf(hash, newLeaves); + return new HashCollisionNode(hash, newLeaves); } - public Leaf find(int hash, Object key){ + public LeafNode find(int hash, Object key){ int idx = findIndex(hash, key); if(idx != -1) return leaves[idx]; |