/* Copyright (c) 2008, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. There is NO WARRANTY for this software. See license.txt for details. */ package java.util; public class HashMap implements Map { private static final int MinimumCapacity = 16; private int size; private Cell[] array; private final Helper helper; public HashMap(int capacity, Helper helper) { if (capacity > 0) { array = new Cell[nextPowerOfTwo(capacity)]; } this.helper = helper; } public HashMap(int capacity) { this(capacity, new MyHelper()); } public HashMap() { this(0); } public HashMap(Map map) { this(map.size()); for (Map.Entry entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } public String toString() { return Collections.toString(this); } private static int nextPowerOfTwo(int n) { int r = 1; while (r < n) r <<= 1; return r; } public boolean isEmpty() { return size() == 0; } public int size() { return size; } private void grow() { if (array == null || size >= array.length * 2) { resize(array == null ? MinimumCapacity : array.length * 2); } } private void shrink() { if (array.length / 2 >= MinimumCapacity && size <= array.length / 3) { resize(array.length / 2); } else if (size == 0) { resize(0); } } private void resize(int capacity) { Cell[] newArray = null; if (capacity != 0) { capacity = nextPowerOfTwo(capacity); if (array != null && array.length == capacity) { return; } newArray = new Cell[capacity]; if (array != null) { for (int i = 0; i < array.length; ++i) { Cell next; for (Cell c = array[i]; c != null; c = next) { next = c.next(); int index = c.hashCode() & (capacity - 1); c.setNext(newArray[index]); newArray[index] = c; } } } } array = newArray; } private Cell find(Object key) { if (array != null) { int index = helper.hash(key) & (array.length - 1); for (Cell c = array[index]; c != null; c = c.next()) { if (helper.equal(key, c.getKey())) { return c; } } } return null; } private void insert(Cell cell) { ++ size; grow(); int index = cell.hashCode() & (array.length - 1); cell.setNext(array[index]); array[index] = cell; } public void remove(Cell cell) { int index = cell.hashCode() & (array.length - 1); Cell p = null; for (Cell c = array[index]; c != null; c = c.next()) { if (c == cell) { if (p == null) { array[index] = c.next(); } else { p.setNext(c.next()); } -- size; break; } } shrink(); } private Cell putCell(K key, V value) { Cell c = find(key); if (c == null) { insert(helper.make(key, value, null)); } else { c.setValue(value); } return c; } public boolean containsKey(Object key) { return find(key) != null; } public boolean containsValue(Object value) { if (array != null) { int index = array.length - 1; for (Cell c = array[index]; c != null; c = c.next()) { if (helper.equal(value, c.getValue())) { return true; } } } return false; } public V get(Object key) { Cell c = find(key); return (c == null ? null : c.getValue()); } public Cell removeCell(Object key) { Cell old = null; if (array != null) { int index = helper.hash(key) & (array.length - 1); Cell p = null; for (Cell c = array[index]; c != null; c = c.next()) { if (helper.equal(key, c.getKey())) { old = c; if (p == null) { array[index] = c.next(); } else { p.setNext(c.next()); } -- size; break; } p = c; } shrink(); } return old; } public V put(K key, V value) { Cell c = find(key); if (c == null) { insert(helper.make(key, value, null)); return null; } else { V old = c.getValue(); c.setValue(value); return old; } } public void putAll(Map elts) { for (Map.Entry entry : elts.entrySet()) { put(entry.getKey(), entry.getValue()); } } public V remove(Object key) { Cell c = removeCell((K)key); return (c == null ? null : c.getValue()); } public void clear() { array = null; size = 0; } public Set> entrySet() { return new EntrySet(); } public Set keySet() { return new KeySet(); } public Collection values() { return new Values(); } Iterator> iterator() { return new MyIterator(); } interface Cell extends Entry { public HashMap.Cell next(); public void setNext(HashMap.Cell next); } interface Helper { public Cell make(K key, V value, Cell next); public int hash(K key); public boolean equal(K a, K b); } private static class MyCell implements Cell { public final K key; public V value; public Cell next; public int hashCode; public MyCell(K key, V value, Cell next, int hashCode) { this.key = key; this.value = value; this.next = next; this.hashCode = hashCode; } public K getKey() { return key; } public V getValue() { return value; } public void setValue(V value) { this.value = value; } public HashMap.Cell next() { return next; } public void setNext(HashMap.Cell next) { this.next = next; } public int hashCode() { return hashCode; } } static class MyHelper implements Helper { public Cell make(K key, V value, Cell next) { return new MyCell(key, value, next, hash(key)); } public int hash(K a) { return (a == null ? 0 : a.hashCode()); } public boolean equal(K a, K b) { return (a == null && b == null) || (a != null && a.equals(b)); } } private class EntrySet implements Set> { public int size() { return HashMap.this.size(); } public boolean isEmpty() { return HashMap.this.isEmpty(); } public boolean contains(Object o) { return (o instanceof Entry) && containsKey(((Entry)o).getKey()); } public boolean add(Entry e) { return putCell(e.getKey(), e.getValue()) != null; } public boolean addAll(Collection> collection) { boolean change = false; for (Entry e: collection) if (add(e)) change = true; return change; } public boolean remove(Object o) { return (o instanceof Entry) && remove((Entry)o); } public boolean remove(Entry e) { return removeCell(e.getKey()) != null; } public T[] toArray(T[] array) { return Collections.toArray(this, array); } public void clear() { HashMap.this.clear(); } public Iterator> iterator() { return new MyIterator(); } } private class KeySet implements Set { public int size() { return HashMap.this.size(); } public boolean isEmpty() { return HashMap.this.isEmpty(); } public boolean contains(Object key) { return containsKey(key); } public boolean add(K key) { return putCell(key, null) != null; } public boolean addAll(Collection collection) { boolean change = false; for (K k: collection) if (add(k)) change = true; return change; } public boolean remove(Object key) { return removeCell(key) != null; } public T[] toArray(T[] array) { return Collections.toArray(this, array); } public void clear() { HashMap.this.clear(); } public Iterator iterator() { return new Collections.KeyIterator(new MyIterator()); } } private class Values implements Collection { public int size() { return HashMap.this.size(); } public boolean isEmpty() { return HashMap.this.isEmpty(); } public boolean contains(Object value) { return containsValue(value); } public boolean add(V value) { throw new UnsupportedOperationException(); } public boolean addAll(Collection collection) { throw new UnsupportedOperationException(); } public boolean remove(Object value) { throw new UnsupportedOperationException(); } public T[] toArray(T[] array) { return Collections.toArray(this, array); } public void clear() { HashMap.this.clear(); } public Iterator iterator() { return new Collections.ValueIterator(new MyIterator()); } } private class MyIterator implements Iterator> { private int currentIndex = -1; private int nextIndex = -1; private Cell previousCell; private Cell currentCell; private Cell nextCell; public MyIterator() { hasNext(); } public Entry next() { if (hasNext()) { if (currentCell != null && currentCell.next() != null) { previousCell = currentCell; } else { previousCell = null; } currentCell = nextCell; currentIndex = nextIndex; nextCell = nextCell.next(); return currentCell; } else { throw new NoSuchElementException(); } } public boolean hasNext() { if (array != null) { while (nextCell == null && ++ nextIndex < array.length) { if (array[nextIndex] != null) { nextCell = array[nextIndex]; return true; } } } return nextCell != null; } public void remove() { if (currentCell != null) { if (previousCell == null) { array[currentIndex] = currentCell.next(); } else { previousCell.setNext(currentCell.next()); if (previousCell.next() == null) { previousCell = null; } } currentCell = null; } else { throw new IllegalStateException(); } } } }