@@ -36,10 +36,21 @@ abstract class CompactHashMapClass<K, V> {
3636 // "new String" is required to avoid clashing with regular strings
3737 public static final String REMOVED_OBJECT = new String ("Non existing mapping value" );
3838
39+ // dexx does not support null, so we wrap null
40+ private static final Object NULL = new Object ();
41+
3942 public CompactHashMapClass (com .github .andrewoma .dexx .collection .Map <K , Integer > key2slot ) {
4043 this .key2slot = key2slot ;
4144 }
4245
46+ private K maskNull (K key ) {
47+ return key == null ? (K ) NULL : key ;
48+ }
49+
50+ private K unmaskNull (K key ) {
51+ return key == NULL ? null : key ;
52+ }
53+
4354 protected Map <K , V > getDefaultValues () {
4455 return Collections .emptyMap ();
4556 }
@@ -52,9 +63,10 @@ public V get(CompactHashMap<K, V> map, K key) {
5263 }
5364
5465 private Object getInternal (CompactHashMap <K , V > map , Object key ) {
55- final Integer slot = key2slot .get ((K ) key );
66+ K nonNullKey = maskNull ((K ) key );
67+ final Integer slot = key2slot .get (nonNullKey );
5668 if (slot == null )
57- return getDefaultValues ().get (key );
69+ return getDefaultValues ().get (nonNullKey );
5870
5971 return getValueFromSlot (map , slot );
6072 }
@@ -73,13 +85,14 @@ protected static Object getValueFromSlot(CompactHashMap map, int slot) {
7385 }
7486
7587 public V put (CompactHashMap <K , V > map , K key , Object value ) {
76- Integer slot = key2slot .get (key );
88+ K nonNullKey = maskNull (key );
89+ Integer slot = key2slot .get (nonNullKey );
7790 Object prevValue = REMOVED_OBJECT ;
7891 if (slot == null ) {
79- prevValue = getDefaultValues ().get (key );
92+ prevValue = getDefaultValues ().get (nonNullKey );
8093
8194 // Try put value as "default"
82- Map <K , V > newDef = CompactHashMapDefaultValues .getNewDefaultValues (getDefaultValues (), key , value );
95+ Map <K , V > newDef = CompactHashMapDefaultValues .getNewDefaultValues (getDefaultValues (), nonNullKey , value );
8396 if (newDef != null ) {
8497 map .klass = getMapWithEmptyDefaults ().getNewDefaultClass (newDef );
8598 return (V ) prevValue ;
@@ -88,7 +101,7 @@ public V put(CompactHashMap<K, V> map, K key, Object value) {
88101 if (value == REMOVED_OBJECT )
89102 return (V ) prevValue ;
90103 // The value is not default -- put using regular way
91- slot = createNewSlot (map , key );
104+ slot = createNewSlot (map , nonNullKey );
92105 }
93106
94107 switch (slot ) {
@@ -173,9 +186,10 @@ private int removedSlotsCount(CompactHashMap<K, V> map) {
173186 public boolean containsKey (CompactHashMap <K , V > map , Object key ) {
174187 // We cannot use plain getInternal here since we will be unable to distinguish
175188 // existing, but null default value
176- final Integer slot = key2slot .get ((K ) key );
189+ K nonNullKey = maskNull ((K ) key );
190+ final Integer slot = key2slot .get (nonNullKey );
177191 if (slot == null )
178- return getDefaultValues ().containsKey (key );
192+ return getDefaultValues ().containsKey (nonNullKey );
179193
180194 return getValueFromSlot (map , slot ) != REMOVED_OBJECT ;
181195 }
@@ -202,7 +216,7 @@ public void serialize(final CompactHashMap<K, V> map, final ObjectOutputStream s
202216 for (Pair <K , Integer > entry : key2slot ) {
203217 Object value = getValueFromSlot (map , entry .component2 ());
204218 if (value == REMOVED_OBJECT ) continue ;
205- s .writeObject (entry .component1 ());
219+ s .writeObject (unmaskNull ( entry .component1 () ));
206220 s .writeObject (value );
207221 }
208222
@@ -273,11 +287,6 @@ public int size() {
273287 return map .size ();
274288 }
275289
276- @ Override
277- public boolean contains (Object o ) {
278- return map .containsValue (o );
279- }
280-
281290 @ Override
282291 public Iterator <V > iterator () {
283292 return new ValueIterator <K , V >(map );
@@ -306,8 +315,13 @@ public boolean contains(Object o) {
306315 if (!(o instanceof Map .Entry ))
307316 return false ;
308317 Map .Entry <K , V > e = (Map .Entry <K , V >) o ;
309- Object mapValue = map .get (e .getKey ());
310- return mapValue == e .getValue () || mapValue != null && mapValue .equals (e .getValue ());
318+ K key = e .getKey ();
319+ V value = e .getValue ();
320+ V ourValue = map .get (key );
321+ if (value == null ) {
322+ return ourValue == null && map .containsKey (key );
323+ }
324+ return value .equals (ourValue );
311325 }
312326
313327 @ Override
@@ -381,7 +395,11 @@ public Map.Entry<K, V> nextEntry() {
381395 }
382396
383397 public void remove () {
398+ if (current == null ) {
399+ throw new IllegalStateException ();
400+ }
384401 map .remove (current .getKey ());
402+ current = null ;
385403 }
386404 }
387405
@@ -428,7 +446,7 @@ public SimpleEntry(CompactHashMap<K, V> map, K key, V value) {
428446 }
429447
430448 public K getKey () {
431- return key ;
449+ return map . klass . unmaskNull ( key ) ;
432450 }
433451
434452 public V getValue () {
@@ -439,6 +457,29 @@ public V setValue(V value) {
439457 this .value = value ;
440458 return map .put (key , value );
441459 }
460+
461+ private static boolean eq (Object o1 , Object o2 ) {
462+ return o1 == null ? o2 == null : o1 .equals (o2 );
463+ }
464+
465+ public boolean equals (Object o ) {
466+ if (!(o instanceof Map .Entry )) {
467+ return false ;
468+ }
469+ Map .Entry <?, ?> e = (Map .Entry <?, ?>) o ;
470+
471+ return eq (getKey (), e .getKey ()) && eq (value , e .getValue ());
472+ }
473+
474+ public int hashCode () {
475+ return (key == NULL ? 0 : key .hashCode ()) ^
476+ (value == null ? 0 : value .hashCode ());
477+ }
478+
479+ @ Override
480+ public String toString () {
481+ return map .klass .unmaskNull (key ) + "=" + value ;
482+ }
442483 }
443484
444485}
0 commit comments