/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.util; import java.util.Iterator; import java.util.NoSuchElementException; import org.jikesrvm.VM; /** * Stripped down implementation of HashMap data structure for use * by core parts of the JikesRVM runtime. * * TODO: This should be a final class; rewrite subclasses to let us do that. */ public class VM_HashMap<K, V> { private static final int DEFAULT_SIZE = 7; private static final float LOAD = 3; /* bias to save space by default */ private Bucket<K, V>[] buckets; private int numElems = 0; public VM_HashMap() { this(DEFAULT_SIZE); } @SuppressWarnings("unchecked") // the java generic array problem private Bucket<K, V>[] newBucketArray(int size) { return new Bucket[size]; } public VM_HashMap(int size) { buckets = newBucketArray(size); } public final int size() { return numElems; } public final V get(K key) { int bucketIdx = bucketIndex(key, buckets.length); Bucket<K, V> cur = buckets[bucketIdx]; while (cur != null && !cur.key.equals(key)) { cur = cur.next; } if (cur == null) { return null; } else { return cur.value; } } public final V put(K key, V value) { if (numElems > (buckets.length * LOAD)) { growMap(); } int bucketIdx = bucketIndex(key, buckets.length); Bucket<K, V> cur = buckets[bucketIdx]; while (cur != null && !cur.key.equals(key)) { cur = cur.next; } if (cur != null) { // replacing existing <key,value> pair V tmp = cur.value; cur.value = value; return tmp; } else { Bucket<K, V> newBucket = new Bucket<K, V>(key, value); newBucket.next = buckets[bucketIdx]; buckets[bucketIdx] = newBucket; numElems++; return null; } } private void growMap() { Bucket<K, V>[] newBuckets = newBucketArray(buckets.length * 2 + 1); for (Bucket<K, V> cur : buckets) { while (cur != null) { Bucket<K, V> next = cur.next; int newIdx = bucketIndex(cur.key, newBuckets.length); cur.next = newBuckets[newIdx]; newBuckets[newIdx] = cur; cur = next; } } buckets = newBuckets; } public final V remove(K key) { int bucketIdx = bucketIndex(key, buckets.length); Bucket<K, V> cur = buckets[bucketIdx]; Bucket<K, V> prev = null; while (cur != null && !cur.key.equals(key)) { prev = cur; cur = cur.next; } if (cur != null) { if (prev == null) { // removing first bucket in chain. buckets[bucketIdx] = cur.next; } else { prev.next = cur.next; } numElems--; return cur.value; } else { return null; } } public final Iterator<V> valueIterator() { return new ValueIterator(); } public final Iterator<K> keyIterator() { return new KeyIterator(); } private int bucketIndex(K key, int divisor) { if (key == null) { return 0; } else { return (key.hashCode() & 0x7fffffff) % divisor; } } private static final class Bucket<K, V> { final K key; V value; Bucket<K, V> next; Bucket(K k, V v) { key = k; value = v; } } /** * Iterator types for key and value */ private class BucketIterator { private int bucketIndex = 0; private Bucket<K, V> next = null; private Bucket<K, V> last = null; private int numVisited = 0; public Bucket<K, V> nextBucket() { if (!hasNext()) { throw new NoSuchElementException(); } while (next == null) { next = buckets[bucketIndex++]; } Bucket<K, V> ans = next; next = ans.next; numVisited++; return ans; } public boolean hasNext() { return numVisited < numElems; } public void remove() { if (last == null) { throw new IllegalStateException(); } VM_HashMap.this.remove(last.key); last = null; } } private final class KeyIterator extends BucketIterator implements Iterator<K> { public K next() { Bucket<K, V> cur = nextBucket(); return cur.key; } } private final class ValueIterator extends BucketIterator implements Iterator<V> { public V next() { Bucket<K, V> cur = nextBucket(); return cur.value; } } /** * These two methods allow VM_HashMaps to be used in the Java 5 for loop. */ /** * @return a java.lang.Iterable for the values in the hash map */ public final Iterable<V> values() { return new Iterable<V>() { public Iterator<V> iterator() { return VM_HashMap.this.valueIterator(); } }; } /** * * @return a java.lang.Iterable for the values in the hash map */ public final Iterable<K> keys() { return new Iterable<K>() { public Iterator<K> iterator() { return VM_HashMap.this.keyIterator(); } }; } }