| /* |
| * 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; |
| } |
| } |