Use the LinkedHashTreeMap instead of LinkedTreeMap and delete the LinkedTreeMap version.
This commit is contained in:
parent
7d53113ebc
commit
af6aa5f782
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import com.google.gson.internal.LinkedTreeMap;
|
import com.google.gson.internal.LinkedHashTreeMap;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -30,8 +30,8 @@ import java.util.Set;
|
|||||||
* @author Joel Leitch
|
* @author Joel Leitch
|
||||||
*/
|
*/
|
||||||
public final class JsonObject extends JsonElement {
|
public final class JsonObject extends JsonElement {
|
||||||
private final LinkedTreeMap<String, JsonElement> members =
|
private final LinkedHashTreeMap<String, JsonElement> members =
|
||||||
new LinkedTreeMap<String, JsonElement>();
|
new LinkedHashTreeMap<String, JsonElement>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JsonObject deepCopy() {
|
JsonObject deepCopy() {
|
||||||
|
@ -188,7 +188,7 @@ public final class ConstructorConstructor {
|
|||||||
} else {
|
} else {
|
||||||
return new ObjectConstructor<T>() {
|
return new ObjectConstructor<T>() {
|
||||||
public T construct() {
|
public T construct() {
|
||||||
return (T) new LinkedTreeMap<String, Object>();
|
return (T) new LinkedHashTreeMap<String, Object>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,496 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.gson.internal;
|
|
||||||
|
|
||||||
import java.io.ObjectStreamException;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.AbstractSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map with a {@code Comparable} key that is implemented as a red-black tree.
|
|
||||||
*
|
|
||||||
* <p>A red-black tree offers quicker insert operations than AVL trees; however, slower "find"
|
|
||||||
* operations.
|
|
||||||
*
|
|
||||||
* <p>This implementation was derived from the JDK's TreeMap class.
|
|
||||||
*/
|
|
||||||
public class LinkedTreeMap<K extends Comparable<K>, V>
|
|
||||||
extends AbstractMap<K, V> implements Serializable {
|
|
||||||
private static final boolean BLACK = false;
|
|
||||||
private static final boolean RED = true;
|
|
||||||
|
|
||||||
// Size stored as a field for optimization instead of recursing tree.
|
|
||||||
private transient int size = 0;
|
|
||||||
|
|
||||||
private transient TreeNode<K, V> root;
|
|
||||||
|
|
||||||
// Store the head and tail to preserve the ordering of nodes inserted into tree
|
|
||||||
private transient TreeNode<K, V> head;
|
|
||||||
private transient TreeNode<K, V> tail;
|
|
||||||
|
|
||||||
public Set<Map.Entry<K, V>> entrySet() {
|
|
||||||
return new EntrySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean containsKey(K key) {
|
|
||||||
return (find(key) != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public V get(K key) {
|
|
||||||
TreeNode<K, V> entry = find(key);
|
|
||||||
return (entry == null) ? null : entry.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public V put(K key, V value) {
|
|
||||||
$Gson$Preconditions.checkNotNull(key);
|
|
||||||
if (root == null) {
|
|
||||||
root = new TreeNode<K, V>(null, null, key, value);
|
|
||||||
head = root;
|
|
||||||
tail = root;
|
|
||||||
size++;
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return findAndUpdateOrCreateNode(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private V findAndUpdateOrCreateNode(K key, V value) {
|
|
||||||
TreeNode<K, V> parent;
|
|
||||||
int lastCompare;
|
|
||||||
|
|
||||||
TreeNode<K, V> entry = root;
|
|
||||||
do {
|
|
||||||
parent = entry;
|
|
||||||
lastCompare = key.compareTo(entry.key);
|
|
||||||
if (lastCompare < 0) {
|
|
||||||
entry = entry.left;
|
|
||||||
} else if (lastCompare > 0) {
|
|
||||||
entry = entry.right;
|
|
||||||
} else {
|
|
||||||
V rval = entry.getValue();
|
|
||||||
entry.setValue(value);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
} while (entry != null);
|
|
||||||
|
|
||||||
size++;
|
|
||||||
|
|
||||||
// Create a new node and set up the tree edges
|
|
||||||
TreeNode<K, V> newEntry = new TreeNode<K, V>(parent, tail, key, value);
|
|
||||||
if (lastCompare < 0) {
|
|
||||||
parent.left = newEntry;
|
|
||||||
} else if (lastCompare > 0) {
|
|
||||||
parent.right = newEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
tail.next = newEntry;
|
|
||||||
tail = newEntry;
|
|
||||||
rebalanceAfterInsert(newEntry);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rebalanceAfterInsert(TreeNode<K, V> x) {
|
|
||||||
x.color = RED;
|
|
||||||
|
|
||||||
while (x != null && x != root && x.parent.color == RED) {
|
|
||||||
if (x.parent == leftOf(parentOf(parentOf(x)))) {
|
|
||||||
TreeNode<K, V> y = rightOf(parentOf(parentOf(x)));
|
|
||||||
if (colorOf(y) == RED) {
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(y, BLACK);
|
|
||||||
setColor(parentOf(parentOf(x)), RED);
|
|
||||||
x = parentOf(parentOf(x));
|
|
||||||
} else {
|
|
||||||
if (x == rightOf(parentOf(x))) {
|
|
||||||
x= parentOf(x);
|
|
||||||
rotateLeft(x);
|
|
||||||
}
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(parentOf(parentOf(x)), RED);
|
|
||||||
rotateRight(parentOf(parentOf(x)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNode<K, V> y = leftOf(parentOf(parentOf(x)));
|
|
||||||
if (colorOf(y) == RED) {
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(y, BLACK);
|
|
||||||
setColor(parentOf(parentOf(x)), RED);
|
|
||||||
x = parentOf(parentOf(x));
|
|
||||||
} else {
|
|
||||||
if (x == leftOf(parentOf(x))) {
|
|
||||||
x = parentOf(x);
|
|
||||||
rotateRight(x);
|
|
||||||
}
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(parentOf(parentOf(x)), RED);
|
|
||||||
rotateLeft(parentOf(parentOf(x)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
root.color = BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> TreeNode<K, V> parentOf(TreeNode<K, V> e) {
|
|
||||||
return (e != null ? e.parent : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> boolean colorOf(TreeNode<K, V> e) {
|
|
||||||
return (e != null ? e.color : BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> TreeNode<K, V> leftOf(TreeNode<K, V> e) {
|
|
||||||
return (e != null ? e.left : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> TreeNode<K, V> rightOf(TreeNode<K, V> e) {
|
|
||||||
return (e != null ? e.right : null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> void setColor(TreeNode<K, V> e, boolean c) {
|
|
||||||
if (e != null){
|
|
||||||
e.color = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K extends Comparable<K>, V> TreeNode<K, V> successor(TreeNode<K, V> t) {
|
|
||||||
if (t == null) {
|
|
||||||
return null;
|
|
||||||
} else if (t.right != null) {
|
|
||||||
TreeNode<K, V> p = t.right;
|
|
||||||
while (p.left != null) {
|
|
||||||
p = p.left;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
} else {
|
|
||||||
TreeNode<K, V> p = t.parent;
|
|
||||||
TreeNode<K, V> ch = t;
|
|
||||||
while (p != null && ch == p.right) {
|
|
||||||
ch = p;
|
|
||||||
p = p.parent;
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rotateLeft(TreeNode<K, V> p) {
|
|
||||||
if (p != null) {
|
|
||||||
TreeNode<K, V> r = p.right;
|
|
||||||
p.right = r.left;
|
|
||||||
if (r.left != null) {
|
|
||||||
r.left.parent = p;
|
|
||||||
}
|
|
||||||
r.parent = p.parent;
|
|
||||||
if (p.parent == null) {
|
|
||||||
root = r;
|
|
||||||
} else if (p.parent.left == p) {
|
|
||||||
p.parent.left = r;
|
|
||||||
} else {
|
|
||||||
p.parent.right = r;
|
|
||||||
}
|
|
||||||
r.left = p;
|
|
||||||
p.parent = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void rotateRight(TreeNode<K, V> p) {
|
|
||||||
if (p != null) {
|
|
||||||
TreeNode<K, V> l = p.left;
|
|
||||||
p.left = l.right;
|
|
||||||
if (l.right != null) {
|
|
||||||
l.right.parent = p;
|
|
||||||
}
|
|
||||||
l.parent = p.parent;
|
|
||||||
if (p.parent == null) {
|
|
||||||
root = l;
|
|
||||||
} else if (p.parent.right == p) {
|
|
||||||
p.parent.right = l;
|
|
||||||
} else {
|
|
||||||
p.parent.left = l;
|
|
||||||
}
|
|
||||||
l.right = p;
|
|
||||||
p.parent = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public V remove(K key) {
|
|
||||||
TreeNode<K, V> entry = find(key);
|
|
||||||
if (entry == null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
size--;
|
|
||||||
V rval = entry.getValue();
|
|
||||||
preserveOrderForRemoval(entry);
|
|
||||||
removeNode(entry);
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeNode(TreeNode<K, V> p) {
|
|
||||||
if (p.left != null && p.right != null) {
|
|
||||||
TreeNode<K, V> s = successor(p);
|
|
||||||
p.key = s.key;
|
|
||||||
p.value = s.value;
|
|
||||||
p = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNode<K, V> replacement = (p.left != null ? p.left : p.right);
|
|
||||||
if (replacement != null) {
|
|
||||||
// Link replacement to parent
|
|
||||||
replacement.parent = p.parent;
|
|
||||||
if (p.parent == null) {
|
|
||||||
root = replacement;
|
|
||||||
} else if (p == p.parent.left) {
|
|
||||||
p.parent.left = replacement;
|
|
||||||
} else {
|
|
||||||
p.parent.right = replacement;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Null out links so they are OK to use by fixAfterDeletion.
|
|
||||||
p.left = null;
|
|
||||||
p.right = null;
|
|
||||||
p.parent = null;
|
|
||||||
|
|
||||||
// Fix replacement
|
|
||||||
if (p.color == BLACK) {
|
|
||||||
fixAfterDeletion(replacement);
|
|
||||||
}
|
|
||||||
} else if (p.parent == null) { // return if we are the only node.
|
|
||||||
root = null;
|
|
||||||
} else { // No children. Use self as phantom replacement and unlink.
|
|
||||||
if (p.color == BLACK) {
|
|
||||||
fixAfterDeletion(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.parent != null) {
|
|
||||||
if (p == p.parent.left) {
|
|
||||||
p.parent.left = null;
|
|
||||||
} else if (p == p.parent.right) {
|
|
||||||
p.parent.right = null;
|
|
||||||
}
|
|
||||||
p.parent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void preserveOrderForRemoval(TreeNode<K, V> p) {
|
|
||||||
// Preserve insertion order for entry set iteration
|
|
||||||
if (p == head) {
|
|
||||||
head = p.next;
|
|
||||||
}
|
|
||||||
if (p == tail) {
|
|
||||||
tail = p.previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNode<K, V> previousNode = p.previous;
|
|
||||||
TreeNode<K, V> nextNode = p.next;
|
|
||||||
if (previousNode != null) {
|
|
||||||
previousNode.next = nextNode;
|
|
||||||
}
|
|
||||||
if (nextNode != null) {
|
|
||||||
nextNode.previous = previousNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fixAfterDeletion(TreeNode<K, V> x) {
|
|
||||||
while (x != root && colorOf(x) == BLACK) {
|
|
||||||
if (x == leftOf(parentOf(x))) {
|
|
||||||
TreeNode<K, V> sib = rightOf(parentOf(x));
|
|
||||||
|
|
||||||
if (colorOf(sib) == RED) {
|
|
||||||
setColor(sib, BLACK);
|
|
||||||
setColor(parentOf(x), RED);
|
|
||||||
rotateLeft(parentOf(x));
|
|
||||||
sib = rightOf(parentOf(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorOf(leftOf(sib)) == BLACK &&
|
|
||||||
colorOf(rightOf(sib)) == BLACK) {
|
|
||||||
setColor(sib, RED);
|
|
||||||
x = parentOf(x);
|
|
||||||
} else {
|
|
||||||
if (colorOf(rightOf(sib)) == BLACK) {
|
|
||||||
setColor(leftOf(sib), BLACK);
|
|
||||||
setColor(sib, RED);
|
|
||||||
rotateRight(sib);
|
|
||||||
sib = rightOf(parentOf(x));
|
|
||||||
}
|
|
||||||
setColor(sib, colorOf(parentOf(x)));
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(rightOf(sib), BLACK);
|
|
||||||
rotateLeft(parentOf(x));
|
|
||||||
x = root;
|
|
||||||
}
|
|
||||||
} else { // symmetric
|
|
||||||
TreeNode<K, V> sib = leftOf(parentOf(x));
|
|
||||||
|
|
||||||
if (colorOf(sib) == RED) {
|
|
||||||
setColor(sib, BLACK);
|
|
||||||
setColor(parentOf(x), RED);
|
|
||||||
rotateRight(parentOf(x));
|
|
||||||
sib = leftOf(parentOf(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colorOf(rightOf(sib)) == BLACK &&
|
|
||||||
colorOf(leftOf(sib)) == BLACK) {
|
|
||||||
setColor(sib, RED);
|
|
||||||
x = parentOf(x);
|
|
||||||
} else {
|
|
||||||
if (colorOf(leftOf(sib)) == BLACK) {
|
|
||||||
setColor(rightOf(sib), BLACK);
|
|
||||||
setColor(sib, RED);
|
|
||||||
rotateLeft(sib);
|
|
||||||
sib = leftOf(parentOf(x));
|
|
||||||
}
|
|
||||||
setColor(sib, colorOf(parentOf(x)));
|
|
||||||
setColor(parentOf(x), BLACK);
|
|
||||||
setColor(leftOf(sib), BLACK);
|
|
||||||
rotateRight(parentOf(x));
|
|
||||||
x = root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setColor(x, BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If somebody is unlucky enough to have to serialize one of these, serialize
|
|
||||||
* it as a LinkedHashMap so that they won't need Gson on the other side to
|
|
||||||
* deserialize it. Using serialization defeats our DoS defence, so most apps
|
|
||||||
* shouldn't use it.
|
|
||||||
*/
|
|
||||||
private Object writeReplace() throws ObjectStreamException {
|
|
||||||
return new LinkedHashMap<K, V>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TreeNode<K, V> find(K key) {
|
|
||||||
if (key != null) {
|
|
||||||
for (TreeNode<K, V> entry = root; entry != null; ) {
|
|
||||||
int compareVal = key.compareTo(entry.key);
|
|
||||||
if (compareVal < 0) {
|
|
||||||
entry = entry.left;
|
|
||||||
} else if (compareVal > 0) {
|
|
||||||
entry = entry.right;
|
|
||||||
} else {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TreeNode<K extends Comparable<K>, V> implements Map.Entry<K, V> {
|
|
||||||
private K key;
|
|
||||||
private V value;
|
|
||||||
private TreeNode<K, V> parent;
|
|
||||||
private TreeNode<K, V> left;
|
|
||||||
private TreeNode<K, V> right;
|
|
||||||
|
|
||||||
// Used for rebalance tree
|
|
||||||
private boolean color = BLACK;
|
|
||||||
|
|
||||||
// This is used for preserving the insertion order
|
|
||||||
private TreeNode<K, V> next;
|
|
||||||
private TreeNode<K, V> previous;
|
|
||||||
|
|
||||||
TreeNode(TreeNode<K, V> parent, TreeNode<K, V> previous, K key, V value) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.previous = previous;
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public K getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'd like to make this throw an UnsupportedOperationException; however,
|
|
||||||
public V setValue(V value) {
|
|
||||||
V rval = this.value;
|
|
||||||
this.value = value;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean equals(Object o) {
|
|
||||||
if (!(o instanceof Entry)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Entry<?, ?> e = (Entry<?, ?>) o;
|
|
||||||
Object eValue = e.getValue();
|
|
||||||
return key.equals(e.getKey())
|
|
||||||
&& (value == null ? eValue == null : value.equals(eValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int hashCode() {
|
|
||||||
return key.hashCode() ^ (value == null ? 0 : value.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String toString() {
|
|
||||||
return key + "=" + value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EntrySet extends AbstractSet<Entry<K, V>> {
|
|
||||||
@Override
|
|
||||||
public Iterator<Map.Entry<K, V>> iterator() {
|
|
||||||
return new LinkedTreeIterator(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return LinkedTreeMap.this.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LinkedTreeIterator implements Iterator<Map.Entry<K, V>> {
|
|
||||||
private TreeNode<K, V> current;
|
|
||||||
|
|
||||||
private LinkedTreeIterator(TreeNode<K, V> first) {
|
|
||||||
this.current = first;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return current != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map.Entry<K, V> next() {
|
|
||||||
TreeNode<K, V> rval = current;
|
|
||||||
current = current.next;
|
|
||||||
return rval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void remove() {
|
|
||||||
LinkedTreeMap.this.remove(current.getKey());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,7 +19,7 @@ package com.google.gson.internal.bind;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
import com.google.gson.TypeAdapterFactory;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.internal.LinkedTreeMap;
|
import com.google.gson.internal.LinkedHashTreeMap;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonToken;
|
import com.google.gson.stream.JsonToken;
|
||||||
@ -64,7 +64,7 @@ public final class ObjectTypeAdapter extends TypeAdapter<Object> {
|
|||||||
return list;
|
return list;
|
||||||
|
|
||||||
case BEGIN_OBJECT:
|
case BEGIN_OBJECT:
|
||||||
Map<String, Object> map = new LinkedTreeMap<String, Object>();
|
Map<String, Object> map = new LinkedHashTreeMap<String, Object>();
|
||||||
in.beginObject();
|
in.beginObject();
|
||||||
while (in.hasNext()) {
|
while (in.hasNext()) {
|
||||||
map.put(in.nextName(), read(in));
|
map.put(in.nextName(), read(in));
|
||||||
|
@ -16,14 +16,16 @@
|
|||||||
|
|
||||||
package com.google.gson.internal;
|
package com.google.gson.internal;
|
||||||
|
|
||||||
|
import com.google.gson.common.MoreAsserts;
|
||||||
import com.google.gson.internal.LinkedHashTreeMap.AvlBuilder;
|
import com.google.gson.internal.LinkedHashTreeMap.AvlBuilder;
|
||||||
import com.google.gson.internal.LinkedHashTreeMap.AvlIterator;
|
import com.google.gson.internal.LinkedHashTreeMap.AvlIterator;
|
||||||
import com.google.gson.internal.LinkedHashTreeMap.Node;
|
import com.google.gson.internal.LinkedHashTreeMap.Node;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
public final class LinkedHashTreeMapTest extends TestCase {
|
public final class LinkedHashTreeMapTest extends TestCase {
|
||||||
public void testIterationOrder() {
|
public void testIterationOrder() {
|
||||||
@ -73,6 +75,22 @@ public final class LinkedHashTreeMapTest extends TestCase {
|
|||||||
assertEquals(0, map.size());
|
assertEquals(0, map.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testEqualsAndHashCode() throws Exception {
|
||||||
|
LinkedHashTreeMap<String, Integer> map1 = new LinkedHashTreeMap<String, Integer>();
|
||||||
|
map1.put("A", 1);
|
||||||
|
map1.put("B", 2);
|
||||||
|
map1.put("C", 3);
|
||||||
|
map1.put("D", 4);
|
||||||
|
|
||||||
|
LinkedHashTreeMap<String, Integer> map2 = new LinkedHashTreeMap<String, Integer>();
|
||||||
|
map2.put("C", 3);
|
||||||
|
map2.put("B", 2);
|
||||||
|
map2.put("D", 4);
|
||||||
|
map2.put("A", 1);
|
||||||
|
|
||||||
|
MoreAsserts.assertEqualsAndHashCode(map1, map2);
|
||||||
|
}
|
||||||
|
|
||||||
public void testAvlWalker() {
|
public void testAvlWalker() {
|
||||||
assertAvlWalker(node(node("a"), "b", node("c")),
|
assertAvlWalker(node(node("a"), "b", node("c")),
|
||||||
"a", "b", "c");
|
"a", "b", "c");
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.gson.internal;
|
|
||||||
|
|
||||||
import com.google.gson.common.MoreAsserts;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for {@code LinkedTreeMap} class.
|
|
||||||
*
|
|
||||||
* @author Joel Leitch
|
|
||||||
*/
|
|
||||||
public class LinkedTreeMapTest extends TestCase {
|
|
||||||
|
|
||||||
public void testPutAndGet() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
map.put("B", 2);
|
|
||||||
map.put("A", 1);
|
|
||||||
map.put("C", 3);
|
|
||||||
|
|
||||||
assertTrue(map.containsKey("A"));
|
|
||||||
assertTrue(map.containsKey("B"));
|
|
||||||
assertTrue(map.containsKey("C"));
|
|
||||||
assertFalse(map.containsKey("D"));
|
|
||||||
|
|
||||||
assertEquals(1, (int) map.get("A"));
|
|
||||||
assertEquals(2, (int) map.get("B"));
|
|
||||||
assertEquals(3, (int) map.get("C"));
|
|
||||||
assertEquals(3, map.entrySet().size());
|
|
||||||
|
|
||||||
assertEquals(1, (int) map.put("A", 4));
|
|
||||||
assertTrue(map.containsKey("A"));
|
|
||||||
assertEquals(4, (int) map.get("A"));
|
|
||||||
assertEquals(3, map.entrySet().size());
|
|
||||||
|
|
||||||
// Ensure entry set size is same as map size
|
|
||||||
assertEquals(map.size(), map.entrySet().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testGetAndContainsNullKey() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
assertFalse(map.containsKey(null));
|
|
||||||
assertNull(map.get(null));
|
|
||||||
|
|
||||||
map.put("A", 1);
|
|
||||||
assertFalse(map.containsKey(null));
|
|
||||||
assertNull(map.get(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testDisallowPutForNullKeys() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
try {
|
|
||||||
map.put(null, 1);
|
|
||||||
fail();
|
|
||||||
} catch (NullPointerException expected) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSingleElement() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
map.put("A", 1);
|
|
||||||
assertEquals(1, map.size());
|
|
||||||
|
|
||||||
assertEquals(1, (int) map.get("A"));
|
|
||||||
map.remove("A");
|
|
||||||
assertEquals(0, map.size());
|
|
||||||
|
|
||||||
// Ensure the map and entry set are empty
|
|
||||||
assertTrue(map.entrySet().isEmpty());
|
|
||||||
assertTrue(map.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAddAndRemove() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
map.put("A", 1);
|
|
||||||
map.put("B", 2);
|
|
||||||
map.put("C", 3);
|
|
||||||
map.put("D", 4);
|
|
||||||
map.put("E", 5);
|
|
||||||
map.put("F", 6);
|
|
||||||
|
|
||||||
assertEquals(3, (int) map.remove("C"));
|
|
||||||
assertEquals(5, map.size());
|
|
||||||
assertIterationOrder(map.entrySet(),
|
|
||||||
new String[] { "A", "B", "D", "E", "F" }, new int[] { 1, 2, 4, 5, 6 });
|
|
||||||
|
|
||||||
// Remove a non-existent key
|
|
||||||
assertNull(map.remove("G"));
|
|
||||||
assertEquals(5, map.size());
|
|
||||||
|
|
||||||
// Remove the first element
|
|
||||||
assertEquals(1, (int) map.remove("A"));
|
|
||||||
assertIterationOrder(map.entrySet(),
|
|
||||||
new String[] { "B", "D", "E", "F" }, new int[] { 2, 4, 5, 6 });
|
|
||||||
|
|
||||||
// Remove the last element
|
|
||||||
assertEquals(6, (int) map.remove("F"));
|
|
||||||
assertIterationOrder(map.entrySet(),
|
|
||||||
new String[] { "B", "D", "E" }, new int[] { 2, 4, 5 });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testInsertionOrderPreserved() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map = new LinkedTreeMap<String, Integer>();
|
|
||||||
String[] keys = { "B", "A", "D", "C", "Z", "W", "E", "F", "T" };
|
|
||||||
int[] values = new int[keys.length];
|
|
||||||
for (int i = 0; i < keys.length; ++i) {
|
|
||||||
values[i] = i;
|
|
||||||
map.put(keys[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Map.Entry<String,Integer>> entries = map.entrySet();
|
|
||||||
assertEquals(keys.length, entries.size());
|
|
||||||
assertIterationOrder(entries, keys, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEqualsAndHashCode() throws Exception {
|
|
||||||
LinkedTreeMap<String, Integer> map1 = new LinkedTreeMap<String, Integer>();
|
|
||||||
map1.put("A", 1);
|
|
||||||
map1.put("B", 2);
|
|
||||||
map1.put("C", 3);
|
|
||||||
map1.put("D", 4);
|
|
||||||
|
|
||||||
LinkedTreeMap<String, Integer> map2 = new LinkedTreeMap<String, Integer>();
|
|
||||||
map2.put("C", 3);
|
|
||||||
map2.put("B", 2);
|
|
||||||
map2.put("D", 4);
|
|
||||||
map2.put("A", 1);
|
|
||||||
|
|
||||||
MoreAsserts.assertEqualsAndHashCode(map1, map2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertIterationOrder(Set<Map.Entry<String, Integer>> entries, String[] keys, int[] values) {
|
|
||||||
int i = 0;
|
|
||||||
for (Iterator<Map.Entry<String, Integer>> iterator = entries.iterator(); iterator.hasNext(); ++i) {
|
|
||||||
Map.Entry<String, Integer> entry = iterator.next();
|
|
||||||
assertEquals(keys[i], entry.getKey());
|
|
||||||
assertEquals(values[i], (int) entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user