diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java index 86bc1cf758..6b19c54f33 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapSerializers.java @@ -50,6 +50,7 @@ import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.TypeDef; +import org.apache.fory.reflect.FieldAccessor; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.ClassResolver; import org.apache.fory.resolver.TypeInfo; @@ -107,23 +108,41 @@ public Map newMap(Map map) { } public static final class LinkedHashMapSerializer extends MapSerializer { + private static final class AccessOrderAccessor { + private static final FieldAccessor INSTANCE = + FieldAccessor.createAccessor( + ReflectionUtils.getDeclaredField(LinkedHashMap.class, "accessOrder")); + } + public LinkedHashMapSerializer(TypeResolver typeResolver) { super(typeResolver, LinkedHashMap.class, true); } + @Override + public Map onMapWrite(WriteContext writeContext, LinkedHashMap value) { + Map map = super.onMapWrite(writeContext, value); + if (!config.isXlang()) { + writeContext.getBuffer().writeBoolean(AccessOrderAccessor.INSTANCE.getBoolean(value)); + } + return map; + } + @Override public LinkedHashMap newMap(ReadContext readContext, boolean entryReadAlwaysAdvances) { MemoryBuffer buffer = readContext.getBuffer(); int numElements = readMapSize(readContext, buffer, entryReadAlwaysAdvances); setNumElements(numElements); - LinkedHashMap hashMap = new LinkedHashMap(numElements); + + boolean accessOrder = !config.isXlang() && buffer.readBoolean(); + LinkedHashMap hashMap = new LinkedHashMap(numElements, 0.75f, accessOrder); readContext.reference(hashMap); return hashMap; } @Override public Map newMap(Map map) { - return new LinkedHashMap(map.size()); + boolean accessOrder = !config.isXlang() && AccessOrderAccessor.INSTANCE.getBoolean(map); + return new LinkedHashMap(map.size(), 0.75f, accessOrder); } } diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java index 0fab5bd62e..333220ccb3 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java @@ -264,6 +264,39 @@ public void basicTestCaseWithMultiConfig( serDeCheck(fory, obj1); } + @Test + public void testLinkedHashMapAccessOrder() { + Fory fory = getJavaFory(); + + LinkedHashMap original = new LinkedHashMap<>(16, 0.75f, true); + original.put(1, 1); + original.put(2, 2); + original.put(3, 3); + original.put(4, 4); + + byte[] bytes = fory.serialize(original); + LinkedHashMap deserialized = + (LinkedHashMap) fory.deserialize(bytes); + + original.get(2); + deserialized.get(2); + + assertEquals(new ArrayList<>(original.keySet()), new ArrayList<>(deserialized.keySet())); + assertEquals( + new ArrayList<>(java.util.Arrays.asList(1, 3, 4, 2)), + new ArrayList<>(deserialized.keySet())); + + original.get(4); + original.get(1); + original.get(3); + + deserialized.get(4); + deserialized.get(1); + deserialized.get(3); + + assertEquals(new ArrayList<>(original.keySet()), new ArrayList<>(deserialized.keySet())); + } + @Test(dataProvider = "referenceTrackingConfig") public void testBasicMap(boolean referenceTrackingConfig) { Fory fory =