Support null values in StringMap

This commit is contained in:
Jesse Wilson 2012-03-11 15:19:01 +00:00
parent ad3489f557
commit 751c69c655
3 changed files with 31 additions and 16 deletions

View File

@ -29,7 +29,8 @@ import java.util.Set;
/** /**
* A map of strings to values. Like LinkedHashMap, this map's iteration order is * A map of strings to values. Like LinkedHashMap, this map's iteration order is
* well defined: it is the order that elements were inserted into the map. * well defined: it is the order that elements were inserted into the map. This
* map does not support null keys.
* *
* <p>This implementation was derived from Android 4.0's LinkedHashMap. * <p>This implementation was derived from Android 4.0's LinkedHashMap.
*/ */
@ -98,32 +99,33 @@ public final class StringMap<K, V> extends AbstractMap<K, V> {
} }
@Override public boolean containsKey(Object key) { @Override public boolean containsKey(Object key) {
return get(key) != null; return getEntry(key) != null;
} }
@Override public V get(Object key) { @Override public V get(Object key) {
LinkedEntry<K, V> entry = getEntry(key);
return entry != null ? entry.value : null;
}
private LinkedEntry<K, V> getEntry(Object key) {
if (key == null) { if (key == null) {
return null; return null;
} }
// Doug Lea's supplemental secondaryHash function (inlined) int hash = secondaryHash(key.hashCode());
int hash = key.hashCode();
hash ^= (hash >>> 20) ^ (hash >>> 12);
hash ^= (hash >>> 7) ^ (hash >>> 4);
LinkedEntry<K, V>[] tab = table; LinkedEntry<K, V>[] tab = table;
for (LinkedEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { for (LinkedEntry<K, V> e = tab[hash & (tab.length - 1)]; e != null; e = e.next) {
K eKey = e.key; K eKey = e.key;
if (eKey == key || (e.hash == hash && key.equals(eKey))) { if (eKey == key || (e.hash == hash && key.equals(eKey))) {
return e.value; return e;
} }
} }
return null; return null;
} }
@Override public V put(K key, V value) { @Override public V put(K key, V value) {
if (key == null || value == null) { if (key == null) {
throw new IllegalArgumentException(); throw new NullPointerException("key == null");
} }
int hash = secondaryHash(key.hashCode()); int hash = secondaryHash(key.hashCode());
@ -311,9 +313,6 @@ public final class StringMap<K, V> extends AbstractMap<K, V> {
} }
public final V setValue(V value) { public final V setValue(V value) {
if (value == null) {
throw new IllegalArgumentException();
}
V oldValue = this.value; V oldValue = this.value;
this.value = value; this.value = value;
return oldValue; return oldValue;
@ -324,7 +323,9 @@ public final class StringMap<K, V> extends AbstractMap<K, V> {
return false; return false;
} }
Entry<?, ?> e = (Entry<?, ?>) o; Entry<?, ?> e = (Entry<?, ?>) o;
return key.equals(e.getKey()) && value.equals(e.getValue()); Object eValue = e.getValue();
return key.equals(e.getKey())
&& (value == null ? eValue == null : value.equals(eValue));
} }
@Override public final int hashCode() { @Override public final int hashCode() {
@ -341,7 +342,7 @@ public final class StringMap<K, V> extends AbstractMap<K, V> {
* exists; otherwise, returns does nothing and returns false. * exists; otherwise, returns does nothing and returns false.
*/ */
private boolean removeMapping(Object key, Object value) { private boolean removeMapping(Object key, Object value) {
if (key == null || value == null) { if (key == null) {
return false; return false;
} }
@ -350,7 +351,7 @@ public final class StringMap<K, V> extends AbstractMap<K, V> {
int index = hash & (tab.length - 1); int index = hash & (tab.length - 1);
for (LinkedEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { for (LinkedEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) {
if (e.hash == hash && key.equals(e.key)) { if (e.hash == hash && key.equals(e.key)) {
if (!value.equals(e.value)) { if (value == null ? e.value != null : !value.equals(e.value)) {
return false; // Map has wrong value for key return false; // Map has wrong value for key
} }
if (prev == null) { if (prev == null) {

View File

@ -63,6 +63,7 @@ public final class ObjectTypeAdapter extends TypeAdapter<Object> {
return list; return list;
case BEGIN_OBJECT: case BEGIN_OBJECT:
// TODO: string map doesn't support null values
Map<String, Object> map = new StringMap<String, Object>(); Map<String, Object> map = new StringMap<String, Object>();
in.beginObject(); in.beginObject();
while (in.hasNext()) { while (in.hasNext()) {

View File

@ -18,6 +18,7 @@ package com.google.gson;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -38,6 +39,18 @@ public final class ObjectTypeAdapterTest extends TestCase {
assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'")); assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'"));
} }
public void testSerializeNullValue() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a", null);
assertEquals("{'a':null}", adapter.toJson(map).replace('"', '\''));
}
public void testDeserializeNullValue() throws Exception {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("a", null);
assertEquals(map, adapter.fromJson("{\"a\":null}"));
}
public void testSerializeObject() throws Exception { public void testSerializeObject() throws Exception {
assertEquals("{}", adapter.toJson(new Object())); assertEquals("{}", adapter.toJson(new Object()));
} }