From 20be4552a87dfd035f3e3e85b3355ecbfcb322ba Mon Sep 17 00:00:00 2001 From: mikera Date: Tue, 8 Oct 2024 08:29:03 +0100 Subject: [PATCH] More HashMap improvements --- .../main/java/convex/core/data/AHashMap.java | 32 +++++++------------ .../src/main/java/convex/core/data/AMap.java | 14 ++++++-- .../main/java/convex/core/data/ARecord.java | 3 +- .../src/main/java/convex/core/data/Cells.java | 3 +- .../src/main/java/convex/core/data/Index.java | 5 +-- .../main/java/convex/core/data/MapLeaf.java | 9 +++--- .../main/java/convex/core/data/MapTree.java | 31 +++++++++++------- .../test/java/convex/core/data/MapsTest.java | 2 -- 8 files changed, 55 insertions(+), 44 deletions(-) diff --git a/convex-core/src/main/java/convex/core/data/AHashMap.java b/convex-core/src/main/java/convex/core/data/AHashMap.java index 5e207e42d..8f6e4b494 100644 --- a/convex-core/src/main/java/convex/core/data/AHashMap.java +++ b/convex-core/src/main/java/convex/core/data/AHashMap.java @@ -1,7 +1,5 @@ package convex.core.data; -import java.util.HashSet; -import java.util.Map; import java.util.function.Function; import java.util.function.Predicate; @@ -20,22 +18,22 @@ public AHashMap empty() { return Maps.empty(); } - /** - * Dissoc given a Ref to the key value. - * @param key Ref of key to remove - * @return Map with specified key removed. - */ - public abstract AHashMap dissocRef(Ref key); - - public abstract AHashMap assocRef(Ref keyRef, V value); - @Override public abstract AHashMap assoc(ACell key, ACell value); + + public abstract AHashMap assocRef(Ref keyRef, V value); + + public abstract AHashMap assocEntry(MapEntry e); @Override public abstract AHashMap dissoc(ACell key); - - public abstract AHashMap assocEntry(MapEntry e); + + /** + * Dissoc given a Hash for the key value. + * @param key Hash of key to remove + * @return Map with specified key removed. + */ + public abstract AHashMap dissocHash(Hash key); /** * Merge another map into this map. Replaces existing entries if they are @@ -171,14 +169,6 @@ public AVector getKeys() { */ protected abstract Hash getFirstHash(); - @Override - public HashSet> entrySet() { - int len = size(); - HashSet> h = new HashSet>(len); - accumulateEntrySet(h); - return h; - } - @Override public AHashMap slice(long start) { return slice(start,count); diff --git a/convex-core/src/main/java/convex/core/data/AMap.java b/convex-core/src/main/java/convex/core/data/AMap.java index 55bcc7d29..8c85b3023 100644 --- a/convex-core/src/main/java/convex/core/data/AMap.java +++ b/convex-core/src/main/java/convex/core/data/AMap.java @@ -1,6 +1,8 @@ package convex.core.data; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; @@ -100,11 +102,11 @@ public final boolean containsValue(Object value) { public abstract MapEntry getKeyRefEntry(Ref ref); /** - * Accumulate all entries from this map in the given mutable Set. + * Accumulate all entries from this map in the given collection * * @param h HashSet in which to accumulate entries */ - protected abstract void accumulateEntrySet(Set> h); + protected abstract void accumulateEntries(Collection> h); /** * Accumulate all keys from this map in the given mutable Set. @@ -267,6 +269,14 @@ public Set keySet() { return ks; } + @Override + public Set> entrySet() { + int len = size(); + HashSet> h = new HashSet>(len); + accumulateEntries(h); + return h; + } + /** * Gets the map entry with the specified hash * diff --git a/convex-core/src/main/java/convex/core/data/ARecord.java b/convex-core/src/main/java/convex/core/data/ARecord.java index d3d39832d..81d2a9521 100644 --- a/convex-core/src/main/java/convex/core/data/ARecord.java +++ b/convex-core/src/main/java/convex/core/data/ARecord.java @@ -1,5 +1,6 @@ package convex.core.data; +import java.util.Collection; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -153,7 +154,7 @@ public MapEntry getKeyRefEntry(Ref keyRef) { } @Override - protected void accumulateEntrySet(Set> h) { + protected void accumulateEntries(Collection> h) { for (long i=0; i T announce(T a, Consumer> noveltyHand */ public static Hash getHash(ACell a) { if (a==null) return Hash.NULL_HASH; - return a.getHash(); + // this picks up a hash in the Ref if available, otherwise populates it for future use + return a.getRef().getHash(); } public static Blob getEncoding(ACell a) { diff --git a/convex-core/src/main/java/convex/core/data/Index.java b/convex-core/src/main/java/convex/core/data/Index.java index 0451e7431..366f5ba28 100644 --- a/convex-core/src/main/java/convex/core/data/Index.java +++ b/convex-core/src/main/java/convex/core/data/Index.java @@ -1,5 +1,6 @@ package convex.core.data; +import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -283,9 +284,9 @@ private ABlobLike getPrefix() { } @Override - protected void accumulateEntrySet(Set> h) { + protected void accumulateEntries(Collection> h) { for (int i = 0; i < children.length; i++) { - children[i].getValue().accumulateEntrySet(h); + children[i].getValue().accumulateEntries(h); } if (entry != null) h.add(entry); } diff --git a/convex-core/src/main/java/convex/core/data/MapLeaf.java b/convex-core/src/main/java/convex/core/data/MapLeaf.java index bf292fbd7..e1bec1e3d 100644 --- a/convex-core/src/main/java/convex/core/data/MapLeaf.java +++ b/convex-core/src/main/java/convex/core/data/MapLeaf.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; @@ -161,10 +162,10 @@ private int seek(K key) { return -1; } - private int seekKeyRef(Ref key) { + private int seekKeyRef(Hash keyHash) { int len = size(); for (int i = 0; i < len; i++) { - if (Utils.equals(key, entries[i].getKeyRef())) return i; + if (Utils.equals(keyHash, entries[i].getKeyHash())) return i; } return -1; } @@ -178,7 +179,7 @@ public MapLeaf dissoc(ACell key) { } @Override - public MapLeaf dissocRef(Ref key) { + public MapLeaf dissocHash(Hash key) { int i = seekKeyRef(key); if (i < 0) return this; // not found return dissocEntry(i); @@ -300,7 +301,7 @@ protected void accumulateValues(java.util.List al) { } @Override - protected void accumulateEntrySet(Set> h) { + protected void accumulateEntries(Collection> h) { for (int i = 0; i < entries.length; i++) { MapEntry me = entries[i]; h.add(me); diff --git a/convex-core/src/main/java/convex/core/data/MapTree.java b/convex-core/src/main/java/convex/core/data/MapTree.java index fbd6346a9..fb176e20a 100644 --- a/convex-core/src/main/java/convex/core/data/MapTree.java +++ b/convex-core/src/main/java/convex/core/data/MapTree.java @@ -1,6 +1,8 @@ package convex.core.data; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.Set; @@ -289,29 +291,33 @@ protected MapEntry getEntryByHash(Hash hash) { return children[i].getValue().getEntryByHash(hash); } - @SuppressWarnings("unchecked") @Override public AHashMap dissoc(ACell key) { - return dissocRef((Ref) Ref.get(key)); + return dissocHash(Cells.getHash(key)); } @Override @SuppressWarnings("unchecked") - public AHashMap dissocRef(Ref keyRef) { - int digit = keyRef.getHash().getHexDigit(shift); + public AHashMap dissocHash(Hash keyHash) { + int digit = keyHash.getHexDigit(shift); int i = Bits.indexForDigit(digit, mask); if (i < 0) return this; // not present // dissoc entry from child AHashMap child = children[i].getValue(); - AHashMap newChild = child.dissocRef(keyRef); + AHashMap newChild = child.dissocHash(keyHash); if (child == newChild) return this; // no removal, no change if (count - 1 == MapLeaf.MAX_ENTRIES) { // reduce to a ListMap - HashSet> eset = entrySet(); - boolean removed = eset.removeIf(e -> Utils.equals(((MapEntry) e).getKeyRef(), keyRef)); - if (!removed) throw new Panic("Expected to remove at least one entry!"); + ArrayList> eset = new ArrayList<>(); + for (int j=0; j c = (i==j)?newChild:children[j].getValue(); + c.accumulateEntries(eset); + } + if (!(eset.size()==MapLeaf.MAX_ENTRIES)) { + throw new Panic("Expected to remove at least one entry!"); + } return MapLeaf.create(eset.toArray((MapEntry[]) MapLeaf.EMPTY_ENTRIES)); } else { // replace child @@ -461,9 +467,9 @@ protected void accumulateValues(java.util.List al) { } @Override - protected void accumulateEntrySet(Set> h) { + protected void accumulateEntries(Collection> h) { for (Ref> mr : children) { - mr.getValue().accumulateEntrySet(h); + mr.getValue().accumulateEntries(h); } } @@ -839,6 +845,9 @@ public void validate() throws InvalidDataException { protected void validateWithPrefix(Hash prefix, int shift) throws InvalidDataException { if (mask == 0) throw new InvalidDataException("TreeMap must have children!", this); int bsize = children.length; + if (bsize<2) { + throw new InvalidDataException("Non-canonical MapTree with child count "+bsize,this); + } long childCount=0;; for (int i = 0; i < bsize; i++) { @@ -848,7 +857,7 @@ protected void validateWithPrefix(Hash prefix, int shift) throws InvalidDataExce ACell o = children[i].getValue(); if (!(o instanceof AHashMap)) { throw new InvalidDataException( - "Expected map child at " + prefix + Utils.toHexChar(digitForIndex(i, mask)), this); + "Expected AHashMap child at " + prefix + Utils.toHexChar(digitForIndex(i, mask)), this); } @SuppressWarnings("unchecked") AHashMap child = (AHashMap) o; diff --git a/convex-core/src/test/java/convex/core/data/MapsTest.java b/convex-core/src/test/java/convex/core/data/MapsTest.java index dbbaac37f..502102cb0 100644 --- a/convex-core/src/test/java/convex/core/data/MapsTest.java +++ b/convex-core/src/test/java/convex/core/data/MapsTest.java @@ -287,8 +287,6 @@ public void testBigMapChild() { @Test public void testBigMapSlice() { - - AHashMap bm=Samples.LONG_MAP_100; AHashMap bm1=bm.slice(0,18); assertEquals(18,bm1.count());