| /* | |
| * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code 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 | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| package sun.jvm.hotspot.debugger; | |
| import java.util.*; | |
| /** | |
| * This is a copy of java.util.HashMap which uses longs as keys | |
| * instead of Objects. It turns out that using this in the PageCache | |
| * implementation speeds up heap traversals by a factor of three. | |
| * | |
| * @author Josh Bloch | |
| * @author Arthur van Hoff | |
| */ | |
| public class LongHashMap | |
| { | |
| static class Entry { | |
| private int hash; | |
| private long key; | |
| private Object value; | |
| private Entry next; | |
| Entry(int hash, long key, Object value, Entry next) { | |
| this.hash = hash; | |
| this.key = key; | |
| this.value = value; | |
| this.next = next; | |
| } | |
| /** | |
| * Returns the key corresponding to this entry. | |
| * | |
| * @return the key corresponding to this entry. | |
| */ | |
| long getKey() { return key; } | |
| /** | |
| * Returns the value corresponding to this entry. If the mapping | |
| * has been removed from the backing map (by the iterator's | |
| * <tt>remove</tt> operation), the results of this call are undefined. | |
| * | |
| * @return the value corresponding to this entry. | |
| */ | |
| Object getValue() { return value; } | |
| /** | |
| * Replaces the value corresponding to this entry with the specified | |
| * value (optional operation). (Writes through to the map.) The | |
| * behavior of this call is undefined if the mapping has already been | |
| * removed from the map (by the iterator's <tt>remove</tt> operation). | |
| * | |
| * @param value new value to be stored in this entry. | |
| * @return old value corresponding to the entry. | |
| * | |
| * @throws UnsupportedOperationException if the <tt>put</tt> operation | |
| * is not supported by the backing map. | |
| * @throws ClassCastException if the class of the specified value | |
| * prevents it from being stored in the backing map. | |
| * @throws IllegalArgumentException if some aspect of this value | |
| * prevents it from being stored in the backing map. | |
| * @throws NullPointerException the backing map does not permit | |
| * <tt>null</tt> values, and the specified value is | |
| * <tt>null</tt>. | |
| */ | |
| Object setValue(Object value) { | |
| Object oldValue = this.value; | |
| this.value = value; | |
| return oldValue; | |
| } | |
| /** | |
| * Compares the specified object with this entry for equality. | |
| * Returns <tt>true</tt> if the given object is also a map entry and | |
| * the two entries represent the same mapping. More formally, two | |
| * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping | |
| * if<pre> | |
| * (e1.getKey()==null ? | |
| * e2.getKey()==null : e1.getKey().equals(e2.getKey())) && | |
| * (e1.getValue()==null ? | |
| * e2.getValue()==null : e1.getValue().equals(e2.getValue())) | |
| * </pre> | |
| * This ensures that the <tt>equals</tt> method works properly across | |
| * different implementations of the <tt>Map.Entry</tt> interface. | |
| * | |
| * @param o object to be compared for equality with this map entry. | |
| * @return <tt>true</tt> if the specified object is equal to this map | |
| * entry. | |
| */ | |
| public boolean equals(Object o) { | |
| if (!(o instanceof Entry)) | |
| return false; | |
| Entry e = (Entry)o; | |
| return (key == e.getKey()) && eq(value, e.getValue()); | |
| } | |
| /** | |
| * Returns the hash code value for this map entry. The hash code | |
| * of a map entry <tt>e</tt> is defined to be: <pre> | |
| * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ | |
| * (e.getValue()==null ? 0 : e.getValue().hashCode()) | |
| * </pre> | |
| * This ensures that <tt>e1.equals(e2)</tt> implies that | |
| * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries | |
| * <tt>e1</tt> and <tt>e2</tt>, as required by the general | |
| * contract of <tt>Object.hashCode</tt>. | |
| * | |
| * @return the hash code value for this map entry. | |
| * @see Object#hashCode() | |
| * @see Object#equals(Object) | |
| * @see #equals(Object) | |
| */ | |
| public int hashCode() { | |
| return hash ^ (value==null ? 0 : value.hashCode()); | |
| } | |
| } | |
| /** | |
| * The hash table data. | |
| */ | |
| transient Entry table[]; | |
| /** | |
| * The total number of mappings in the hash table. | |
| */ | |
| transient int size; | |
| /** | |
| * The table is rehashed when its size exceeds this threshold. (The | |
| * value of this field is (int)(capacity * loadFactor).) | |
| * | |
| * @serial | |
| */ | |
| int threshold; | |
| /** | |
| * The load factor for the hash table. | |
| * | |
| * @serial | |
| */ | |
| final float loadFactor; | |
| /** | |
| * The number of times this HashMap has been structurally modified | |
| * Structural modifications are those that change the number of mappings in | |
| * the HashMap or otherwise modify its internal structure (e.g., | |
| * rehash). This field is used to make iterators on Collection-views of | |
| * the HashMap fail-fast. (See ConcurrentModificationException). | |
| */ | |
| transient int modCount = 0; | |
| /** | |
| * Constructs a new, empty map with the specified initial | |
| * capacity and the specified load factor. | |
| * | |
| * @param initialCapacity the initial capacity of the HashMap. | |
| * @param loadFactor the load factor of the HashMap | |
| * @throws IllegalArgumentException if the initial capacity is less | |
| * than zero, or if the load factor is nonpositive. | |
| */ | |
| public LongHashMap(int initialCapacity, float loadFactor) { | |
| if (initialCapacity < 0) | |
| throw new IllegalArgumentException("Illegal Initial Capacity: "+ | |
| initialCapacity); | |
| if (loadFactor <= 0 || Float.isNaN(loadFactor)) | |
| throw new IllegalArgumentException("Illegal Load factor: "+ | |
| loadFactor); | |
| if (initialCapacity==0) | |
| initialCapacity = 1; | |
| this.loadFactor = loadFactor; | |
| table = new Entry[initialCapacity]; | |
| threshold = (int)(initialCapacity * loadFactor); | |
| } | |
| /** | |
| * Constructs a new, empty map with the specified initial capacity | |
| * and default load factor, which is <tt>0.75</tt>. | |
| * | |
| * @param initialCapacity the initial capacity of the HashMap. | |
| * @throws IllegalArgumentException if the initial capacity is less | |
| * than zero. | |
| */ | |
| public LongHashMap(int initialCapacity) { | |
| this(initialCapacity, 0.75f); | |
| } | |
| /** | |
| * Constructs a new, empty map with a default capacity and load | |
| * factor, which is <tt>0.75</tt>. | |
| */ | |
| public LongHashMap() { | |
| this(11, 0.75f); | |
| } | |
| /** | |
| * Returns the number of key-value mappings in this map. | |
| * | |
| * @return the number of key-value mappings in this map. | |
| */ | |
| public int size() { | |
| return size; | |
| } | |
| /** | |
| * Returns <tt>true</tt> if this map contains no key-value mappings. | |
| * | |
| * @return <tt>true</tt> if this map contains no key-value mappings. | |
| */ | |
| public boolean isEmpty() { | |
| return size == 0; | |
| } | |
| /** | |
| * Returns the value to which this map maps the specified key. Returns | |
| * <tt>null</tt> if the map contains no mapping for this key. A return | |
| * value of <tt>null</tt> does not <i>necessarily</i> indicate that the | |
| * map contains no mapping for the key; it's also possible that the map | |
| * explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt> | |
| * operation may be used to distinguish these two cases. | |
| * | |
| * @return the value to which this map maps the specified key. | |
| * @param key key whose associated value is to be returned. | |
| */ | |
| public Object get(long key) { | |
| Entry e = getEntry(key); | |
| return (e == null ? null : e.value); | |
| } | |
| /** | |
| * Returns <tt>true</tt> if this map contains a mapping for the specified | |
| * key. | |
| * | |
| * @return <tt>true</tt> if this map contains a mapping for the specified | |
| * key. | |
| * @param key key whose presence in this Map is to be tested. | |
| */ | |
| public boolean containsKey(long key) { | |
| return getEntry(key) != null; | |
| } | |
| /** | |
| * Returns the entry associated with the specified key in the | |
| * HashMap. Returns null if the HashMap contains no mapping | |
| * for this key. | |
| */ | |
| Entry getEntry(long key) { | |
| Entry tab[] = table; | |
| int hash = (int) key; | |
| int index = (hash & 0x7FFFFFFF) % tab.length; | |
| for (Entry e = tab[index]; e != null; e = e.next) | |
| if (e.hash == hash && e.key ==key) | |
| return e; | |
| return null; | |
| } | |
| /** | |
| * Returns <tt>true</tt> if this map maps one or more keys to the | |
| * specified value. | |
| * | |
| * @param value value whose presence in this map is to be tested. | |
| * @return <tt>true</tt> if this map maps one or more keys to the | |
| * specified value. | |
| */ | |
| public boolean containsValue(Object value) { | |
| Entry tab[] = table; | |
| if (value==null) { | |
| for (int i = tab.length ; i-- > 0 ;) | |
| for (Entry e = tab[i] ; e != null ; e = e.next) | |
| if (e.value==null) | |
| return true; | |
| } else { | |
| for (int i = tab.length ; i-- > 0 ;) | |
| for (Entry e = tab[i] ; e != null ; e = e.next) | |
| if (value.equals(e.value)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Associates the specified value with the specified key in this map. | |
| * If the map previously contained a mapping for this key, the old | |
| * value is replaced. | |
| * | |
| * @param key key with which the specified value is to be associated. | |
| * @param value value to be associated with the specified key. | |
| * @return previous value associated with specified key, or <tt>null</tt> | |
| * if there was no mapping for key. A <tt>null</tt> return can | |
| * also indicate that the HashMap previously associated | |
| * <tt>null</tt> with the specified key. | |
| */ | |
| public Object put(long key, Object value) { | |
| Entry tab[] = table; | |
| int hash = (int) key; | |
| int index = (hash & 0x7FFFFFFF) % tab.length; | |
| // Look for entry in hash table | |
| for (Entry e = tab[index] ; e != null ; e = e.next) { | |
| if (e.hash == hash && e.key == key) { | |
| Object oldValue = e.value; | |
| e.value = value; | |
| return oldValue; | |
| } | |
| } | |
| // It's not there; grow the hash table if necessary... | |
| modCount++; | |
| if (size >= threshold) { | |
| rehash(); | |
| tab = table; | |
| index = (hash & 0x7FFFFFFF) % tab.length; | |
| } | |
| // ...and add the entry | |
| size++; | |
| tab[index] = newEntry(hash, key, value, tab[index]); | |
| return null; | |
| } | |
| /** | |
| * Removes the mapping for this key from this map if present. | |
| * | |
| * @param key key whose mapping is to be removed from the map. | |
| * @return previous value associated with specified key, or <tt>null</tt> | |
| * if there was no mapping for key. A <tt>null</tt> return can | |
| * also indicate that the map previously associated <tt>null</tt> | |
| * with the specified key. | |
| */ | |
| public Object remove(long key) { | |
| Entry e = removeEntryForKey(key); | |
| return (e == null ? null : e.value); | |
| } | |
| /** | |
| * Removes and returns the entry associated with the specified key | |
| * in the HashMap. Returns null if the HashMap contains no mapping | |
| * for this key. | |
| */ | |
| Entry removeEntryForKey(long key) { | |
| Entry tab[] = table; | |
| int hash = (int) key; | |
| int index = (hash & 0x7FFFFFFF) % tab.length; | |
| for (Entry e = tab[index], prev = null; e != null; | |
| prev = e, e = e.next) { | |
| if (e.hash == hash && e.key == key) { | |
| modCount++; | |
| if (prev != null) | |
| prev.next = e.next; | |
| else | |
| tab[index] = e.next; | |
| size--; | |
| return e; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Removes the specified entry from this HashMap (and increments modCount). | |
| * | |
| * @throws ConcurrentModificationException if the entry is not in the Map | |
| */ | |
| void removeEntry(Entry doomed) { | |
| Entry[] tab = table; | |
| int index = (doomed.hash & 0x7FFFFFFF) % tab.length; | |
| for (Entry e = tab[index], prev = null; e != null; | |
| prev = e, e = e.next) { | |
| if (e == doomed) { | |
| modCount++; | |
| if (prev == null) | |
| tab[index] = e.next; | |
| else | |
| prev.next = e.next; | |
| size--; | |
| return; | |
| } | |
| } | |
| throw new ConcurrentModificationException(); | |
| } | |
| /** | |
| * Removes all mappings from this map. | |
| */ | |
| public void clear() { | |
| Entry tab[] = table; | |
| modCount++; | |
| for (int index = tab.length; --index >= 0; ) | |
| tab[index] = null; | |
| size = 0; | |
| } | |
| /** | |
| * Rehashes the contents of this map into a new <tt>HashMap</tt> instance | |
| * with a larger capacity. This method is called automatically when the | |
| * number of keys in this map exceeds its capacity and load factor. | |
| */ | |
| void rehash() { | |
| Entry oldTable[] = table; | |
| int oldCapacity = oldTable.length; | |
| int newCapacity = oldCapacity * 2 + 1; | |
| Entry newTable[] = new Entry[newCapacity]; | |
| modCount++; | |
| threshold = (int)(newCapacity * loadFactor); | |
| table = newTable; | |
| for (int i = oldCapacity ; i-- > 0 ;) { | |
| for (Entry old = oldTable[i] ; old != null ; ) { | |
| Entry e = old; | |
| old = old.next; | |
| int index = (e.hash & 0x7FFFFFFF) % newCapacity; | |
| e.next = newTable[index]; | |
| newTable[index] = e; | |
| } | |
| } | |
| } | |
| static boolean eq(Object o1, Object o2) { | |
| return (o1==null ? o2==null : o1.equals(o2)); | |
| } | |
| Entry newEntry(int hash, long key, Object value, Entry next) { | |
| return new Entry(hash, key, value, next); | |
| } | |
| int capacity() { | |
| return table.length; | |
| } | |
| float loadFactor() { | |
| return loadFactor; | |
| } | |
| } |