@@ -27,6 +27,13 @@ abstract class CompactHashMapClass<K, V> {
2727 public static final CompactHashMapClass EMPTY = new CompactHashMapClassEmptyDefaults (
2828 new com .github .andrewoma .dexx .collection .HashMap ());
2929
30+ /**
31+ * Enum to represent "value was removed" in the serialized representation of the map.
32+ */
33+ enum RemovedObjectMarker {
34+ INSTANCE ;
35+ }
36+
3037 final com .github .andrewoma .dexx .collection .Map <K , Integer > key2slot ; // Immutable
3138
3239 // This value is used as a marker of deleted object
@@ -70,14 +77,16 @@ private Object getInternal(CompactHashMap<K, V> map, Object key) {
7077
7178 protected static Object getValueFromSlot (CompactHashMap map , int slot ) {
7279 switch (slot ) {
73- case -1 :
74- return map .v1 ;
7580 case -2 :
76- return map .v2 ;
77- case -3 :
7881 return map .v3 ;
82+ case -1 :
83+ return map .v2 ;
84+ case 0 :
85+ // Maps with fewer than 3 keys store their slot 0 in v1
86+ if (map .klass .key2slot .size () <= 3 ) {
87+ return map .v1 ;
88+ }
7989 }
80-
8190 return ((Object []) map .v1 )[slot ];
8291 }
8392
@@ -103,21 +112,23 @@ public V put(CompactHashMap<K, V> map, K key, Object value) {
103112
104113 switch (slot ) {
105114 case -1 :
106- if (prevValue == REMOVED_OBJECT )
107- prevValue = map .v1 ;
108- map .v1 = value ;
109- break ;
110- case -2 :
111115 if (prevValue == REMOVED_OBJECT )
112116 prevValue = map .v2 ;
113117 map .v2 = value ;
114118 break ;
115- case -3 :
119+ case -2 :
116120 if (prevValue == REMOVED_OBJECT )
117121 prevValue = map .v3 ;
118122 map .v3 = value ;
119123 break ;
120124 default :
125+ if (slot == 0 && key2slot .size () <= 3 ) {
126+ if (prevValue == REMOVED_OBJECT )
127+ prevValue = map .v1 ;
128+ map .v1 = value ;
129+ break ;
130+ }
131+
121132 Object [] array = (Object []) map .v1 ;
122133 if (prevValue == REMOVED_OBJECT )
123134 prevValue = array [slot ];
@@ -206,16 +217,26 @@ public Set<Map.Entry<K, V>> entrySet(CompactHashMap<K, V> map) {
206217 public void serialize (final CompactHashMap <K , V > map , final ObjectOutputStream s ) throws IOException {
207218 // We serialize default and non default values separately
208219 // That makes serialized representation more compact when several maps share defaults
209- int size = key2slot .size () - removedSlotsCount (map );
210- s .writeInt (size );
211-
212- if (size > 0 )
213- for (Pair <K , Integer > entry : key2slot ) {
214- Object value = getValueFromSlot (map , entry .component2 ());
215- if (value == REMOVED_OBJECT ) continue ;
216- s .writeObject (unmaskNull (entry .component1 ()));
217- s .writeObject (value );
220+ int ownKeys = key2slot .size ();
221+ s .writeInt (ownKeys );
222+ // Write keys in the order they were added to the map, so deserialization reuses key2slot instances
223+ if (ownKeys > 0 ) {
224+ // Slots are always -2..(map.size-2), so we do not need sort
225+ final Object [] keys = new Object [ownKeys ];
226+ key2slot .forEach (new com .github .andrewoma .dexx .collection .Function <Pair <K , Integer >, Void >() {
227+ public Void invoke (Pair <K , Integer > entry ) {
228+ keys [entry .component2 () + 2 ] = entry .component1 ();
229+ return null ;
230+ }
231+ });
232+ for (int i = 0 ; i < keys .length ; i ++) {
233+ Object key = keys [i ];
234+ Object value = getValueFromSlot (map , i - 2 );
235+ s .writeObject (unmaskNull ((K ) key ));
236+ // We write REMOVED_OBJECT as well to keep the same key2slot when deserializing
237+ s .writeObject (value == REMOVED_OBJECT ? RemovedObjectMarker .INSTANCE : value );
218238 }
239+ }
219240
220241 // Serialize default values as separate map
221242 s .writeObject (getDefaultValues ());
@@ -228,7 +249,14 @@ public static <K, V> void deserialize(CompactHashMap<K, V> map, ObjectInputStrea
228249 for (int i = 0 ; i < size ; i ++) {
229250 K key = (K ) s .readObject ();
230251 V value = (V ) s .readObject ();
231- map .put (key , value );
252+ if (value == RemovedObjectMarker .INSTANCE ) {
253+ // We cannot use put(key, REMOVED_OBJECT) as the map would just ignore it
254+ // We need to add an entry first so it allocates a new slot
255+ map .put (key , null );
256+ map .remove (key );
257+ } else {
258+ map .put (key , value );
259+ }
232260 }
233261
234262 Map <K , V > defaults = (Map <K , V >) s .readObject ();
0 commit comments