1 /* 2 * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 20 * CA 95054 USA or visit www.sun.com if you need additional information or 21 * have any questions. 22 * 23 */ 24 25 package sun.jvm.hotspot.debugger; 26 27 import java.util.*; 28 29 /** 30 * This is a copy of java.util.HashMap which uses longs as keys 31 * instead of Objects. It turns out that using this in the PageCache 32 * implementation speeds up heap traversals by a factor of three. 33 * 34 * @author Josh Bloch 35 * @author Arthur van Hoff 36 */ 37 38 public class LongHashMap 39 { 40 static class Entry { 41 private int hash; 42 private long key; 43 private Object value; 44 private Entry next; 45 46 Entry(int hash, long key, Object value, Entry next) { 47 this.hash = hash; 48 this.key = key; 49 this.value = value; 50 this.next = next; 51 } 52 53 /** 54 * Returns the key corresponding to this entry. 55 * 56 * @return the key corresponding to this entry. 57 */ 58 long getKey() { return key; } 59 60 /** 61 * Returns the value corresponding to this entry. If the mapping 62 * has been removed from the backing map (by the iterator's 63 * <tt>remove</tt> operation), the results of this call are undefined. 64 * 65 * @return the value corresponding to this entry. 66 */ 67 Object getValue() { return value; } 68 69 /** 70 * Replaces the value corresponding to this entry with the specified 71 * value (optional operation). (Writes through to the map.) The 72 * behavior of this call is undefined if the mapping has already been 73 * removed from the map (by the iterator's <tt>remove</tt> operation). 74 * 75 * @param value new value to be stored in this entry. 76 * @return old value corresponding to the entry. 77 * 78 * @throws UnsupportedOperationException if the <tt>put</tt> operation 79 * is not supported by the backing map. 80 * @throws ClassCastException if the class of the specified value 81 * prevents it from being stored in the backing map. 82 * @throws IllegalArgumentException if some aspect of this value 83 * prevents it from being stored in the backing map. 84 * @throws NullPointerException the backing map does not permit 85 * <tt>null</tt> values, and the specified value is 86 * <tt>null</tt>. 87 */ 88 Object setValue(Object value) { 89 Object oldValue = this.value; 90 this.value = value; 91 return oldValue; 92 } 93 94 /** 95 * Compares the specified object with this entry for equality. 96 * Returns <tt>true</tt> if the given object is also a map entry and 97 * the two entries represent the same mapping. More formally, two 98 * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping 99 * if<pre> 100 * (e1.getKey()==null ? 101 * e2.getKey()==null : e1.getKey().equals(e2.getKey())) && 102 * (e1.getValue()==null ? 103 * e2.getValue()==null : e1.getValue().equals(e2.getValue())) 104 * </pre> 105 * This ensures that the <tt>equals</tt> method works properly across 106 * different implementations of the <tt>Map.Entry</tt> interface. 107 * 108 * @param o object to be compared for equality with this map entry. 109 * @return <tt>true</tt> if the specified object is equal to this map 110 * entry. 111 */ 112 public boolean equals(Object o) { 113 if (!(o instanceof Entry)) 114 return false; 115 Entry e = (Entry)o; 116 return (key == e.getKey()) && eq(value, e.getValue()); 117 } 118 119 /** 120 * Returns the hash code value for this map entry. The hash code 121 * of a map entry <tt>e</tt> is defined to be: <pre> 122 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 123 * (e.getValue()==null ? 0 : e.getValue().hashCode()) 124 * </pre> 125 * This ensures that <tt>e1.equals(e2)</tt> implies that 126 * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries 127 * <tt>e1</tt> and <tt>e2</tt>, as required by the general 128 * contract of <tt>Object.hashCode</tt>. 129 * 130 * @return the hash code value for this map entry. 131 * @see Object#hashCode() 132 * @see Object#equals(Object) 133 * @see #equals(Object) 134 */ 135 public int hashCode() { 136 return hash ^ (value==null ? 0 : value.hashCode()); 137 } 138 } 139 140 /** 141 * The hash table data. 142 */ 143 transient Entry table[]; 144 145 /** 146 * The total number of mappings in the hash table. 147 */ 148 transient int size; 149 150 /** 151 * The table is rehashed when its size exceeds this threshold. (The 152 * value of this field is (int)(capacity * loadFactor).) 153 * 154 * @serial 155 */ 156 int threshold; 157 158 /** 159 * The load factor for the hash table. 160 * 161 * @serial 162 */ 163 final float loadFactor; 164 165 /** 166 * The number of times this HashMap has been structurally modified 167 * Structural modifications are those that change the number of mappings in 168 * the HashMap or otherwise modify its internal structure (e.g., 169 * rehash). This field is used to make iterators on Collection-views of 170 * the HashMap fail-fast. (See ConcurrentModificationException). 171 */ 172 transient int modCount = 0; 173 174 /** 175 * Constructs a new, empty map with the specified initial 176 * capacity and the specified load factor. 177 * 178 * @param initialCapacity the initial capacity of the HashMap. 179 * @param loadFactor the load factor of the HashMap 180 * @throws IllegalArgumentException if the initial capacity is less 181 * than zero, or if the load factor is nonpositive. 182 */ 183 public LongHashMap(int initialCapacity, float loadFactor) { 184 if (initialCapacity < 0) 185 throw new IllegalArgumentException("Illegal Initial Capacity: "+ 186 initialCapacity); 187 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 188 throw new IllegalArgumentException("Illegal Load factor: "+ 189 loadFactor); 190 if (initialCapacity==0) 191 initialCapacity = 1; 192 this.loadFactor = loadFactor; 193 table = new Entry[initialCapacity]; 194 threshold = (int)(initialCapacity * loadFactor); 195 } 196 197 /** 198 * Constructs a new, empty map with the specified initial capacity 199 * and default load factor, which is <tt>0.75</tt>. 200 * 201 * @param initialCapacity the initial capacity of the HashMap. 202 * @throws IllegalArgumentException if the initial capacity is less 203 * than zero. 204 */ 205 public LongHashMap(int initialCapacity) { 206 this(initialCapacity, 0.75f); 207 } 208 209 /** 210 * Constructs a new, empty map with a default capacity and load 211 * factor, which is <tt>0.75</tt>. 212 */ 213 public LongHashMap() { 214 this(11, 0.75f); 215 } 216 217 /** 218 * Returns the number of key-value mappings in this map. 219 * 220 * @return the number of key-value mappings in this map. 221 */ 222 public int size() { 223 return size; 224 } 225 226 /** 227 * Returns <tt>true</tt> if this map contains no key-value mappings. 228 * 229 * @return <tt>true</tt> if this map contains no key-value mappings. 230 */ 231 public boolean isEmpty() { 232 return size == 0; 233 } 234 235 /** 236 * Returns the value to which this map maps the specified key. Returns 237 * <tt>null</tt> if the map contains no mapping for this key. A return 238 * value of <tt>null</tt> does not <i>necessarily</i> indicate that the 239 * map contains no mapping for the key; it's also possible that the map 240 * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt> 241 * operation may be used to distinguish these two cases. 242 * 243 * @return the value to which this map maps the specified key. 244 * @param key key whose associated value is to be returned. 245 */ 246 public Object get(long key) { 247 Entry e = getEntry(key); 248 return (e == null ? null : e.value); 249 } 250 251 /** 252 * Returns <tt>true</tt> if this map contains a mapping for the specified 253 * key. 254 * 255 * @return <tt>true</tt> if this map contains a mapping for the specified 256 * key. 257 * @param key key whose presence in this Map is to be tested. 258 */ 259 public boolean containsKey(long key) { 260 return getEntry(key) != null; 261 } 262 263 /** 264 * Returns the entry associated with the specified key in the 265 * HashMap. Returns null if the HashMap contains no mapping 266 * for this key. 267 */ 268 Entry getEntry(long key) { 269 Entry tab[] = table; 270 int hash = (int) key; 271 int index = (hash & 0x7FFFFFFF) % tab.length; 272 273 for (Entry e = tab[index]; e != null; e = e.next) 274 if (e.hash == hash && e.key ==key) 275 return e; 276 277 return null; 278 } 279 280 /** 281 * Returns <tt>true</tt> if this map maps one or more keys to the 282 * specified value. 283 * 284 * @param value value whose presence in this map is to be tested. 285 * @return <tt>true</tt> if this map maps one or more keys to the 286 * specified value. 287 */ 288 public boolean containsValue(Object value) { 289 Entry tab[] = table; 290 291 if (value==null) { 292 for (int i = tab.length ; i-- > 0 ;) 293 for (Entry e = tab[i] ; e != null ; e = e.next) 294 if (e.value==null) 295 return true; 296 } else { 297 for (int i = tab.length ; i-- > 0 ;) 298 for (Entry e = tab[i] ; e != null ; e = e.next) 299 if (value.equals(e.value)) 300 return true; 301 } 302 303 return false; 304 } 305 306 /** 307 * Associates the specified value with the specified key in this map. 308 * If the map previously contained a mapping for this key, the old 309 * value is replaced. 310 * 311 * @param key key with which the specified value is to be associated. 312 * @param value value to be associated with the specified key. 313 * @return previous value associated with specified key, or <tt>null</tt> 314 * if there was no mapping for key. A <tt>null</tt> return can 315 * also indicate that the HashMap previously associated 316 * <tt>null</tt> with the specified key. 317 */ 318 public Object put(long key, Object value) { 319 Entry tab[] = table; 320 int hash = (int) key; 321 int index = (hash & 0x7FFFFFFF) % tab.length; 322 323 // Look for entry in hash table 324 for (Entry e = tab[index] ; e != null ; e = e.next) { 325 if (e.hash == hash && e.key == key) { 326 Object oldValue = e.value; 327 e.value = value; 328 return oldValue; 329 } 330 } 331 332 // It's not there; grow the hash table if necessary... 333 modCount++; 334 if (size >= threshold) { 335 rehash(); 336 tab = table; 337 index = (hash & 0x7FFFFFFF) % tab.length; 338 } 339 340 // ...and add the entry 341 size++; 342 tab[index] = newEntry(hash, key, value, tab[index]); 343 return null; 344 } 345 346 /** 347 * Removes the mapping for this key from this map if present. 348 * 349 * @param key key whose mapping is to be removed from the map. 350 * @return previous value associated with specified key, or <tt>null</tt> 351 * if there was no mapping for key. A <tt>null</tt> return can 352 * also indicate that the map previously associated <tt>null</tt> 353 * with the specified key. 354 */ 355 public Object remove(long key) { 356 Entry e = removeEntryForKey(key); 357 return (e == null ? null : e.value); 358 } 359 360 /** 361 * Removes and returns the entry associated with the specified key 362 * in the HashMap. Returns null if the HashMap contains no mapping 363 * for this key. 364 */ 365 Entry removeEntryForKey(long key) { 366 Entry tab[] = table; 367 int hash = (int) key; 368 int index = (hash & 0x7FFFFFFF) % tab.length; 369 370 for (Entry e = tab[index], prev = null; e != null; 371 prev = e, e = e.next) { 372 if (e.hash == hash && e.key == key) { 373 modCount++; 374 if (prev != null) 375 prev.next = e.next; 376 else 377 tab[index] = e.next; 378 379 size--; 380 return e; 381 } 382 } 383 384 return null; 385 } 386 387 /** 388 * Removes the specified entry from this HashMap (and increments modCount). 389 * 390 * @throws ConcurrentModificationException if the entry is not in the Map 391 */ 392 void removeEntry(Entry doomed) { 393 Entry[] tab = table; 394 int index = (doomed.hash & 0x7FFFFFFF) % tab.length; 395 396 for (Entry e = tab[index], prev = null; e != null; 397 prev = e, e = e.next) { 398 if (e == doomed) { 399 modCount++; 400 if (prev == null) 401 tab[index] = e.next; 402 else 403 prev.next = e.next; 404 size--; 405 return; 406 } 407 } 408 throw new ConcurrentModificationException(); 409 } 410 411 /** 412 * Removes all mappings from this map. 413 */ 414 public void clear() { 415 Entry tab[] = table; 416 modCount++; 417 for (int index = tab.length; --index >= 0; ) 418 tab[index] = null; 419 size = 0; 420 } 421 422 /** 423 * Rehashes the contents of this map into a new <tt>HashMap</tt> instance 424 * with a larger capacity. This method is called automatically when the 425 * number of keys in this map exceeds its capacity and load factor. 426 */ 427 void rehash() { 428 Entry oldTable[] = table; 429 int oldCapacity = oldTable.length; 430 int newCapacity = oldCapacity * 2 + 1; 431 Entry newTable[] = new Entry[newCapacity]; 432 433 modCount++; 434 threshold = (int)(newCapacity * loadFactor); 435 table = newTable; 436 437 for (int i = oldCapacity ; i-- > 0 ;) { 438 for (Entry old = oldTable[i] ; old != null ; ) { 439 Entry e = old; 440 old = old.next; 441 442 int index = (e.hash & 0x7FFFFFFF) % newCapacity; 443 e.next = newTable[index]; 444 newTable[index] = e; 445 } 446 } 447 } 448 449 static boolean eq(Object o1, Object o2) { 450 return (o1==null ? o2==null : o1.equals(o2)); 451 } 452 453 Entry newEntry(int hash, long key, Object value, Entry next) { 454 return new Entry(hash, key, value, next); 455 } 456 457 int capacity() { 458 return table.length; 459 } 460 461 float loadFactor() { 462 return loadFactor; 463 } 464 }