001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.util; 014 015 import java.util.Iterator; 016 import java.util.NoSuchElementException; 017 018 import org.jikesrvm.VM; 019 import org.jikesrvm.mm.mminterface.MemoryManager; 020 021 /** 022 * Common super class for all VM hash maps. 023 */ 024 abstract class AbstractHashMapRVM<K, V> { 025 026 protected static final int DEFAULT_SIZE = 7; 027 private static final float LOAD = 3; 028 protected AbstractBucket<K, V>[] buckets; 029 protected int numElems = 0; 030 031 abstract static class AbstractBucket<K, V> { 032 abstract AbstractBucket<K, V> getNext(); 033 034 /** 035 * Change the next bucket after this bucket, possibly constructing a new 036 * abstract bucket. 037 */ 038 abstract AbstractBucket<K, V> setNext(AbstractBucket<K, V> n); 039 040 abstract K getKey(); 041 042 abstract V getValue(); 043 044 abstract void setValue(V v); 045 } 046 047 /** 048 * Are two keys the same? 049 */ 050 abstract boolean same(K key1, K key2); 051 052 abstract int hashTheKey(K key); 053 054 abstract AbstractBucket<K,V> createNewBucket(K k, V v, AbstractBucket<K,V> n); 055 056 AbstractHashMapRVM(int size) { 057 buckets = newBucketArray(size); 058 } 059 060 public final int size() { 061 return numElems; 062 } 063 064 public final V get(K key) { 065 if (key == null) { 066 return null; 067 } 068 int bucketIdx = bucketIndex(key, buckets.length); 069 AbstractBucket<K, V> cur = buckets[bucketIdx]; 070 while (cur != null && !same(cur.getKey(), key)) { 071 cur = cur.getNext(); 072 } 073 if (cur == null) { 074 return null; 075 } else { 076 return cur.getValue(); 077 } 078 } 079 080 /** 081 * Advise against growing the buckets if they are immortal, as it will lead 082 * to multiple sets of buckets that will be scanned 083 */ 084 private boolean growMapAllowed() { 085 return !VM.runningVM || !MemoryManager.isImmortal(buckets); 086 } 087 088 public final V put(K key, V value) { 089 if (VM.VerifyAssertions) VM._assert(key != null); 090 if (growMapAllowed() && numElems > (buckets.length * LOAD)) { 091 growMap(); 092 } 093 094 int bucketIdx = bucketIndex(key, buckets.length); 095 AbstractBucket<K, V> cur = buckets[bucketIdx]; 096 while (cur != null && !same(cur.getKey(), key)) { 097 cur = cur.getNext(); 098 } 099 if (cur != null) { 100 // replacing existing <key,value> pair 101 V tmp = cur.getValue(); 102 cur.setValue(value); 103 return tmp; 104 } else { 105 buckets[bucketIdx] = createNewBucket(key, value, buckets[bucketIdx]); 106 numElems++; 107 return null; 108 } 109 } 110 111 @SuppressWarnings("unchecked") 112 private AbstractBucket<K, V>[] newBucketArray(int size) { 113 return new AbstractBucket[size]; 114 } 115 116 private void growMap() { 117 AbstractBucket<K, V>[] newBuckets = newBucketArray(buckets.length * 2 + 1); 118 // Iterate over all buckets 119 for (AbstractBucket<K, V> cur : buckets) { 120 // Iterate over all values in a bucket 121 while (cur != null) { 122 AbstractBucket<K, V> next = cur.getNext(); 123 int newIdx = bucketIndex(cur.getKey(), newBuckets.length); 124 cur = cur.setNext(newBuckets[newIdx]); 125 newBuckets[newIdx] = cur; 126 cur = next; 127 } 128 } 129 buckets = newBuckets; 130 } 131 132 public V remove(K key) { 133 if (VM.VerifyAssertions) VM._assert(key != null); 134 int bucketIdx = bucketIndex(key, buckets.length); 135 AbstractBucket<K, V> cur = buckets[bucketIdx]; 136 AbstractBucket<K, V> prev = null; 137 while (cur != null && !same(cur.getKey(), key)) { 138 prev = cur; 139 cur = cur.getNext(); 140 } 141 if (cur != null) { 142 if (prev == null) { 143 // removing first bucket in chain. 144 buckets[bucketIdx] = cur.getNext(); 145 } else { 146 AbstractBucket<K, V> newPrev = prev.setNext(cur.getNext()); 147 if (newPrev != prev) { 148 throw new UnsupportedOperationException(); 149 } 150 } 151 numElems--; 152 return cur.getValue(); 153 } else { 154 return null; 155 } 156 } 157 158 public final Iterator<V> valueIterator() { 159 return new ValueIterator(); 160 } 161 162 public final Iterator<K> keyIterator() { 163 return new KeyIterator(); 164 } 165 166 private int bucketIndex(K key, int divisor) { 167 if (key == null) { 168 return 0; 169 } else { 170 return (hashTheKey(key) & 0x7fffffff) % divisor; 171 } 172 } 173 174 /** 175 * @return a java.lang.Iterable for the values in the hash map 176 */ 177 public final Iterable<V> values() { 178 return new Iterable<V>() { 179 @Override 180 public Iterator<V> iterator() { 181 return AbstractHashMapRVM.this.valueIterator(); 182 } 183 }; 184 } 185 186 /** 187 * 188 * @return a java.lang.Iterable for the values in the hash map 189 */ 190 public final Iterable<K> keys() { 191 return new Iterable<K>() { 192 @Override 193 public Iterator<K> iterator() { 194 return AbstractHashMapRVM.this.keyIterator(); 195 } 196 }; 197 } 198 199 /** 200 * Iterator types for key and value 201 */ 202 private class BucketIterator { 203 private int bucketIndex = 0; 204 private AbstractBucket<K, V> next = null; 205 private int numVisited = 0; 206 207 public AbstractBucket<K, V> nextBucket() { 208 if (!hasNext()) { 209 throw new NoSuchElementException(); 210 } 211 212 while (next == null) { 213 next = buckets[bucketIndex++]; 214 } 215 AbstractBucket<K, V> ans = next; 216 next = ans.getNext(); 217 numVisited++; 218 return ans; 219 } 220 221 public boolean hasNext() { 222 return numVisited < numElems; 223 } 224 225 public void remove() { 226 throw new UnsupportedOperationException(); 227 } 228 } 229 230 private final class KeyIterator extends BucketIterator implements Iterator<K> { 231 @Override 232 public K next() { 233 AbstractBucket<K, V> cur = nextBucket(); 234 return cur.getKey(); 235 } 236 } 237 238 private final class ValueIterator extends BucketIterator implements Iterator<V> { 239 @Override 240 public V next() { 241 AbstractBucket<K, V> cur = nextBucket(); 242 return cur.getValue(); 243 } 244 } 245 }