diff --git a/convex-core/src/main/java/convex/core/data/AIndex.java b/convex-core/src/main/java/convex/core/data/AIndex.java index c355e8997..d4fc00dc8 100644 --- a/convex-core/src/main/java/convex/core/data/AIndex.java +++ b/convex-core/src/main/java/convex/core/data/AIndex.java @@ -1,7 +1,7 @@ package convex.core.data; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import convex.core.data.type.AType; @@ -49,9 +49,13 @@ public boolean containsKey(ACell key) { */ public abstract V get(K key); + // entryAt(i) walks the radix tree in ascending key order, so a + // LinkedHashSet (which preserves insertion order) is needed here to keep + // that ordering visible through entrySet() — a plain HashSet iterates in + // hash-bucket order and silently discards it (#651). @Override public Set> entrySet() { - HashSet> hs=new HashSet<>(size()); + LinkedHashSet> hs=new LinkedHashSet<>(size()); long n=count(); for (long i=0; i me=entryAt(i); diff --git a/convex-core/src/test/java/convex/core/data/IndexTest.java b/convex-core/src/test/java/convex/core/data/IndexTest.java index 6e08a1242..0f62ffd20 100644 --- a/convex-core/src/test/java/convex/core/data/IndexTest.java +++ b/convex-core/src/test/java/convex/core/data/IndexTest.java @@ -235,6 +235,27 @@ public void testSymbolicKeys() throws InvalidDataException { doIndexTests(rm); } + /** + * entrySet() must preserve the Index's sorted key order (#651) — the class + * is documented as "a sorted radix-tree map... provide sorted orderings for + * indexes", and callers commonly iterate entrySet() (e.g. `for (var e : + * index.entrySet())`) expecting that guarantee to hold, not just entryAt(i). + */ + @Test + public void testEntrySetOrder() throws InvalidDataException { + Index m = Index.none(); + for (int i = 0; i < 50; i++) { + m = m.assoc(LongBlob.create(i), RT.cvm((long) i)); + } + assertEquals(50L, m.count()); + + java.util.Iterator> it = m.entrySet().iterator(); + for (long i = 0; i < m.count(); i++) { + assertEquals(m.entryAt(i), it.next(), "entrySet() order diverged from entryAt() sorted order"); + } + assertFalse(it.hasNext()); + } + @Test public void testContains() { Index bm=Samples.INT_INDEX_256; long n=bm.count;