I made a map based on a binary tree ordered by hashcodes, with collisions fixed with equals()
. Are there any improvements I could make?
public class HashTreeMap<K, V> implements Map<K, V> {
Optional<Node<K, V>> root = Optional.empty();
@Override
public V put(final K key, final V value) {
// Returns previous value associated with key
if (root.isPresent()) {
return root.get().put(key, value);
} else {
root = Node.createNode(key, value);
return null;
}
}
@Override
public V get(final Object key) {
if (root.isPresent()) {
return root.get().get(key);
} else {
return null;
}
}
@Override
public int size() {
if (root.isPresent()) {
return root.get().size();
} else {
return 0;
}
}
@Override
public boolean isEmpty() {
return !root.isPresent();
}
@Override
public boolean containsKey(final Object key) {
// TODO a better method, this could easily be improved
final Set<K> keys = keySet();
return keys.contains(key);
}
@Override
public boolean containsValue(final Object value) {
// TODO a better method, this could easily be improved
final Collection<V> keys = values();
return keys.contains(value);
}
@Override
public V remove(final Object key) {
if (root.isPresent()) {
return root.get().remove(key);
} else {
return null;
}
}
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
for (final Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
@Override
public void clear() {
this.root = Optional.empty();
}
@Override
public Set<K> keySet() {
// TODO a better method, this could easily be improved
final Set<Map.Entry<K, V>> entries = entrySet();
final Set<K> keys = new HashSet<>(entries.size());
for (final Map.Entry<K, V> entry : entries) {
keys.add(entry.getKey());
}
return keys;
}
@Override
public Collection<V> values() {
// TODO a better method, this could easily be improved
final Set<Map.Entry<K, V>> entries = entrySet();
final List<V> values = new ArrayList<>(entries.size());
for (final Map.Entry<K, V> entry : entries) {
values.add(entry.getValue());
}
return values;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
if (root.isPresent()) {
final Set<Map.Entry<K, V>> set = new HashSet<>();
root.get().addAllEntries(set);
return set;
} else {
return new HashSet<>();
}
}
@Override
public String toString() {
return "Tree{" + (root.isPresent() ? root.get().toString() : "") + "}";
}
private static class Node<K, V> {
Optional<Node<K, V>> left = Optional.empty();
Optional<Node<K, V>> right = Optional.empty();
final int id;
List<Entry<K, V>> entries = new LinkedList<>();
public Node(final int id) {
this.id = id;
}
public int size() {
int leftSize = 0;
int rightSize = 0;
if (left.isPresent()) {
leftSize = left.get().size();
}
if (right.isPresent()) {
rightSize = right.get().size();
}
return entries.size() + leftSize + rightSize;
}
public V put(final K key, final V value) {
V prevVal = null;
final int idKey = key.hashCode();
if (id == idKey) {
final Iterator<Entry<K, V>> it = entries.iterator();
// check for overlaps
while (it.hasNext()) {
final Entry<K, V> e = it.next();
if (e.key.equals(key)) {
prevVal = e.value;
it.remove();
}
}
entries.add(new Entry<>(key, value));
} else if (idKey < id) {
if (left.isPresent()) {
prevVal = left.get().put(key, value);
} else {
left = createNode(key, value);
prevVal = null;
}
} else if (idKey > id) {
if (right.isPresent()) {
prevVal = right.get().put(key, value);
} else {
right = createNode(key, value);
prevVal = null;
}
}
return prevVal;
}
public V get(final Object key) {
final int idKey = key.hashCode();
if (id == idKey) {
final Iterator<Entry<K, V>> it = entries.iterator();
// check for overlaps
while (it.hasNext()) {
final Entry<K, V> e = it.next();
if (e.key.equals(key)) {
return e.value;
}
}
// we found a matching hashcode, but no equal node
return null;
} else if (idKey < id) {
if (left.isPresent()) {
return left.get().get(key);
} else {
return null;
}
} else {
// hashcode is greater than current node
if (right.isPresent()) {
return right.get().get(key);
} else {
return null;
}
}
}
public V remove(final Object key) {
final int keyId = key.hashCode();
if (keyId == id) {
final Iterator<Entry<K, V>> it = entries.iterator();
while (it.hasNext()) {
final Entry<K, V> e = it.next();
if (e.key.equals(key)) {
it.remove();
return e.value;
}
}
// matching hashcode, but no matching equals
return null;
} else if (keyId < id) {
if (left.isPresent()) {
return left.get().remove(key);
} else {
return null;
}
} else {
if (right.isPresent()) {
return right.get().remove(key);
} else {
return null;
}
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (final Entry<K, V> e : entries) {
sb.append(e).append(',');
}
return (left.isPresent() ? left.get().toString() : "")
+ sb.toString()
+ (right.isPresent() ? right.get().toString() : "");
}
public static <K, V> Optional<Node<K, V>> createNode(final K key,
final V value) {
final Node<K, V> node = new Node<>(key.hashCode());
node.entries.add(new Entry<>(key, value));
return Optional.of(node);
}
public void addAllEntries(final Collection<Map.Entry<K, V>> s) {
s.addAll(entries);
if (left.isPresent()) {
left.get().addAllEntries(s);
}
if (right.isPresent()) {
right.get().addAllEntries(s);
}
}
private static class Entry<K, V> implements Map.Entry<K, V> {
private final K key;
private V value;
public Entry(final K key, final V value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return key + "=>" + value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(final V value) {
final V old = this.value;
this.value = value;
return old;
}
}
}
}