/* * This file is a part of Alchemy OS project. * Copyright (C) 2011-2014, Sergey Basalaev <sbasalaev@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package alchemy.util; /** * Maps keys to values. * <p> * Unlike Hashtable this class is not synchronized. * * @author Sergey Basalaev */ public final class HashMap { private HashMapEntry[] entries; private int size; /** Creates new empty map. */ public HashMap() { entries = new HashMapEntry[11]; } /** Returns value assigned to given key. * Returns null if key is not in the map. */ public Object get(Object key) { HashMapEntry[] table = entries; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (HashMapEntry e = table[index]; e != null; e = e.next) { if (e.hash == hash && e.key.equals(key)) { return e.value; } } return null; } /** Sets new value to given key. */ public void set(Object key, Object value) { if (value == null) { this.remove(key); return; } // search existing entry HashMapEntry[] table = entries; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (HashMapEntry e = table[index]; e != null; e = e.next) { if (e.hash == hash && e.key.equals(key)) { e.value = value; return; } } // rehash if too many objects if (size * 3 / 4 > table.length) { rehash(); table = entries; index = (hash & 0x7FFFFFFF) % table.length; } // create new entry HashMapEntry e = new HashMapEntry(); e.hash = hash; e.key = key; e.value = value; e.next = table[index]; table[index] = e; size++; } /** Removes key and the corresponding value from the map. */ public void remove(Object key) { HashMapEntry[] table = entries; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % table.length; for (HashMapEntry e = table[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && e.key.equals(key)) { if (prev != null) prev.next = e.next; else table[index] = e.next; size--; } } } /** Returns number of keys in this map. */ public int size() { return size; } /** Removes all keys and values from this map. */ public void clear() { HashMapEntry[] table = entries; for (int i=table.length-1; i >= 0; i--) { table[i] = null; } size = 0; } /** Returns all keys this map contains as object array. */ public Object[] keys() { Object[] ret = new Object[size]; int pos = 0; HashMapEntry[] table = entries; for (int i=table.length-1; i>=0; i--) { for (HashMapEntry e = table[i]; e != null; e = e.next) { ret[pos] = e.key; pos++; } } return ret; } private void rehash() { HashMapEntry[] oldTable = entries; HashMapEntry[] newTable = new HashMapEntry[oldTable.length * 2 + 1]; for (int index=oldTable.length-1; index >= 0; index--) { HashMapEntry list = oldTable[index]; while (list != null) { HashMapEntry e = list; list = list.next; int newIndex = (e.hash & 0x7FFFFFFF) % newTable.length; e.next = newTable[newIndex]; newTable[newIndex] = e; } } entries = newTable; } void buildString(ArrayList dejaVu, StringBuffer buf) { if (dejaVu.indexOf(this, 0) >= 0) { buf.append("{...}"); } else { dejaVu.add(this); buf.append('{'); HashMapEntry[] table = entries; boolean first = true; for (int index = table.length-1; index >= 0; index--) { for (HashMapEntry e = table[index]; e != null; e = e.next) { if (first) first = false; else buf.append(", "); Strings.buildString(e.key, dejaVu, buf); buf.append('='); Strings.buildString(e.value, dejaVu, buf); } } buf.append('}'); dejaVu.remove(this); } } public String toString() { StringBuffer buf = new StringBuffer(); buildString(new ArrayList(), buf); return buf.toString(); } private static class HashMapEntry { int hash; Object key; Object value; HashMapEntry next; } }