3 import java.io.IOException;
4 import java.util.AbstractSet;
5 import java.util.ConcurrentModificationException;
6 import java.util.Iterator;
7 import java.util.NoSuchElementException;
9 import java.util.concurrent.atomic.AtomicInteger;
13 import dstm2.factory.Factory;
15 public class IntHashMap implements Iterable<Integer>{
18 * The default initial capacity - MUST be a power of two.
20 static final int DEFAULT_INITIAL_CAPACITY = 16;
23 * The maximum capacity, used if a higher value is implicitly specified
24 * by either of the constructors with arguments.
25 * MUST be a power of two <= 1<<30.
27 static final int MAXIMUM_CAPACITY = 1 << 30;
30 * The load factor used when none specified in constructor.
32 static final float DEFAULT_LOAD_FACTOR = 0.75f;
35 * The table, resized as necessary. Length MUST Always be a power of two.
37 //transient TEntry<V>[] table;
38 dstm2.AtomicArray<TEntry> table;
39 final private Factory<TEntry> factory;
41 //transient Entry<K, V>[] table;
44 * The number of key-value mappings contained in this identity hash map.
48 java.util.concurrent.atomic.AtomicInteger size;
51 * The capacity of the table
53 transient int capacity;
56 * The next size value at which to resize (capacity * load factor).
62 * The load factor for the hash table.
66 final float loadFactor;
69 * The number of times this HashMap has been structurally modified
70 * Structural modifications are those that change the number of mappings in
71 * the HashMap or otherwise modify its internal structure (e.g.,
72 * rehash). This field is used to make iterators on Collection-views of
73 * the HashMap fail-fast. (See ConcurrentModificationException).
75 transient volatile int modCount;
78 * Constructs an empty <tt>HashMap</tt> with the specified initial
79 * capacity and load factor.
81 * @param initialCapacity The initial capacity.
82 * @param loadFactor The load factor.
83 * @throws IllegalArgumentException if the initial capacity is negative
84 * or the load factor is nonpositive.
86 public IntHashMap(int initialCapacity, float loadFactor) {
87 size = new AtomicInteger(0);
88 factory = Thread.makeFactory(TEntry.class);
89 if (initialCapacity < 0)
90 throw new IllegalArgumentException("Illegal initial capacity: " +
92 if (initialCapacity > MAXIMUM_CAPACITY)
93 initialCapacity = MAXIMUM_CAPACITY;
94 if (loadFactor <= 0 || Float.isNaN(loadFactor))
95 throw new IllegalArgumentException("Illegal load factor: " +
98 // Find a power of 2 >= initialCapacity
100 while (capacity < initialCapacity)
103 this.capacity = capacity;
104 this.loadFactor = loadFactor;
105 threshold = (int)(capacity * loadFactor);
106 table = new dstm2.AtomicArray<TEntry>(TEntry.class, capacity);
107 // for(int i = 0; i < capacity; i++) {
108 // table[i] = factory.create();
110 //table = new Entry[capacity];
115 * Constructs an empty <tt>HashMap</tt> with the specified initial
116 * capacity and the default load factor (0.75).
118 * @param initialCapacity the initial capacity.
119 * @throws IllegalArgumentException if the initial capacity is negative.
121 public IntHashMap(int initialCapacity) {
122 this(initialCapacity, DEFAULT_LOAD_FACTOR);
126 * Constructs an empty <tt>HashMap</tt> with the default initial capacity
127 * (16) and the default load factor (0.75).
129 public IntHashMap() {
130 this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
134 * Constructs a new <tt>HashMap</tt> with the same mappings as the
135 * specified <tt>Map</tt>. The <tt>HashMap</tt> is created with
136 * default load factor (0.75) and an initial capacity sufficient to
137 * hold the mappings in the specified <tt>Map</tt>.
139 * @param m the map whose mappings are to be placed in this map.
140 * @throws NullPointerException if the specified map is null.
142 public IntHashMap(IntHashMap m) {
143 this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
144 DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
148 // internal utilities
151 * Initialization hook for subclasses. This method is called
152 * in all constructors and pseudo-constructors (clone, readObject)
153 * after HashMap has been initialized but before any entries have
154 * been inserted. (In the absence of this method, readObject would
155 * require explicit knowledge of subclasses.)
159 public dstm2.AtomicArray<TEntry> getBuckets() {
163 * Value representing null keys inside tables.
165 static final Object NULL_KEY = new Object();
168 * Returns internal representation for key. Use NULL_KEY if key is null.
170 static <T> T maskNull(T key) {
171 return key == null ? (T)NULL_KEY : key;
175 * Returns key represented by specified internal representation.
177 static <T> T unmaskNull(T key) {
178 return (key == NULL_KEY ? null : key);
182 * Whether to prefer the old supplemental hash function, for
183 * compatibility with broken applications that rely on the
184 * internal hashing order.
186 * Set to true only by hotspot when invoked via
187 * -XX:+UseNewHashFunction or -XX:+AggressiveOpts
189 private static final boolean useNewHash;
190 static { useNewHash = false; }
192 private static int oldHash(int h) {
200 private static int newHash(int h) {
201 // This function ensures that hashCodes that differ only by
202 // constant multiples at each bit position have a bounded
203 // number of collisions (approximately 8 at default load factor).
204 h ^= (h >>> 20) ^ (h >>> 12);
205 return h ^ (h >>> 7) ^ (h >>> 4);
209 * Applies a supplemental hash function to a given hashCode, which
210 * defends against poor quality hash functions. This is critical
211 * because HashMap uses power-of-two length hash tables, that
212 * otherwise encounter collisions for hashCodes that do not differ
215 public static int hash(int h) {
216 return useNewHash ? newHash(h) : oldHash(h);
219 public static int hash(Object key) {
220 return hash(key.hashCode());
224 * Check for equality of non-null reference x and possibly-null y.
226 static boolean eq(Object x, Object y) {
227 return x == y || x.equals(y);
231 * Returns index for hash code h.
233 public static int indexFor(int h, int length) {
234 return h & (length-1);
238 * Returns the number of key-value mappings in this map.
240 * @return the number of key-value mappings in this map.
247 * Returns <tt>true</tt> if this map contains no key-value mappings.
249 * @return <tt>true</tt> if this map contains no key-value mappings.
251 public boolean isEmpty() {
252 return size.get() == 0;
256 * Returns the value to which the specified key is mapped in this identity
257 * hash map, or <tt>null</tt> if the map contains no mapping for this key.
258 * A return value of <tt>null</tt> does not <i>necessarily</i> indicate
259 * that the map contains no mapping for the key; it is also possible that
260 * the map explicitly maps the key to <tt>null</tt>. The
261 * <tt>containsKey</tt> method may be used to distinguish these two cases.
263 * @param key the key whose associated value is to be returned.
264 * @return the value to which this map maps the specified key, or
265 * <tt>null</tt> if the map contains no mapping for this key.
266 * @see #put(Object, Object)
268 public Integer get(int key) {
270 // return getForNullKey();
271 int hash = hash(key);
272 for (TEntry e = table.get(indexFor(hash, capacity));
276 if (e.getHash() == hash)
282 // private V getForNullKey() {
283 // int hash = hash(NULL_KEY.hashCode());
284 // int i = indexFor(hash, capacity);
285 // TEntry<K,V> e = table[i];
286 // //Entry<K,V> e = table[i];
290 // if (e.getKey() == NULL_KEY)
291 // return e.getValue();
297 * Returns <tt>true</tt> if this map contains a mapping for the
300 * @param key The key whose presence in this map is to be tested
301 * @return <tt>true</tt> if this map contains a mapping for the specified
304 public boolean containsKey(int key) {
305 Object k = maskNull(key);
307 int i = indexFor(hash, capacity);
308 TEntry e = table.get(i);
310 if (e.getHash() == hash)
318 * Returns the entry associated with the specified key in the
319 * HashMap. Returns null if the HashMap contains no mapping
322 TEntry getEntry(int key) {
323 int hash = hash(key);
324 int i = indexFor(hash, capacity);
325 TEntry e = table.get(i);
326 while (e != null && !(e.getHash() == hash))
332 * Associates the specified value with the specified key in this map.
333 * If the map previously contained a mapping for this key, the old
336 * @param key key with which the specified value is to be associated.
337 * @param value value to be associated with the specified key.
338 * @return previous value associated with specified key, or <tt>null</tt>
339 * if there was no mapping for key. A <tt>null</tt> return can
340 * also indicate that the HashMap previously associated
341 * <tt>null</tt> with the specified key.
343 public Integer put(int key, Integer value) {
344 int hash = hash(key);
345 int i = indexFor(hash, capacity);
346 for (TEntry e = table.get(i); e != null; e = e.getNext()) {
347 if (e.getHash() == hash) {
348 Integer oldValue = e.getValue();
350 // e.recordAccess(this);
355 addEntry(hash, value, i);
362 * This method is used instead of put by constructors and
363 * pseudoconstructors (clone, readObject). It does not resize the table,
364 * check for comodification, etc. It calls createEntry rather than
367 private void putForCreate(int key, Integer value) {
368 int hash = hash(key);
369 int i = indexFor(hash, capacity);
372 * Look for preexisting entry for key. This will never happen for
373 * clone or deserialize. It will only happen for construction if the
374 * input Map is a sorted map whose ordering is inconsistent w/ equals.
376 for (TEntry e = table.get(i); e != null; e = e.getNext()) {
377 if (e.getHash() == hash) {
383 createEntry(hash, value, i);
386 void putAllForCreate(IntHashMap m) {
387 for (Iterator<? extends IntHashMap.TEntry> i = m.entrySet().iterator(); i.hasNext(); ) {
388 IntHashMap.TEntry e = i.next();
389 putForCreate(e.getHash(), e.getValue());
394 * Rehashes the contents of this map into a new array with a
395 * larger capacity. This method is called automatically when the
396 * number of keys in this map reaches its threshold.
398 * If current capacity is MAXIMUM_CAPACITY, this method does not
399 * resize the map, but sets threshold to Integer.MAX_VALUE.
400 * This has the effect of preventing future calls.
402 * @param newCapacity the new capacity, MUST be a power of two;
403 * must be greater than current capacity unless current
404 * capacity is MAXIMUM_CAPACITY (in which case value
407 void resize(int newCapacity) {
408 dstm2.AtomicArray<TEntry> oldTable = table;
409 int oldCapacity = capacity;
410 if (oldCapacity == MAXIMUM_CAPACITY) {
411 threshold = Integer.MAX_VALUE;
415 dstm2.AtomicArray<TEntry> newTable = new dstm2.AtomicArray<TEntry>(TEntry.class, newCapacity);
416 transfer(newTable, newCapacity);
418 threshold = (int)(newCapacity * loadFactor);
419 capacity = newCapacity;
423 * Transfer all entries from current table to newTable.
425 void transfer(dstm2.AtomicArray<TEntry> newTable, int nc) {
426 dstm2.AtomicArray<TEntry> src = table;
427 int newCapacity = nc;
428 for (int j = 0; j < capacity; j++) {
429 TEntry e = src.get(j);
433 TEntry next = e.getNext();
434 int i = indexFor(e.getHash(), newCapacity);
435 e.setNext(newTable.get(i));
444 * Copies all of the mappings from the specified map to this map
445 * These mappings will replace any mappings that
446 * this map had for any of the keys currently in the specified map.
448 * @param m mappings to be stored in this map.
449 * @throws NullPointerException if the specified map is null.
451 public void putAll(IntHashMap m) {
452 int numKeysToBeAdded = m.size();
453 if (numKeysToBeAdded == 0)
457 * Expand the map if the map if the number of mappings to be added
458 * is greater than or equal to threshold. This is conservative; the
459 * obvious condition is (m.size() + size) >= threshold, but this
460 * condition could result in a map with twice the appropriate capacity,
461 * if the keys to be added overlap with the keys already in this map.
462 * By using the conservative calculation, we subject ourself
463 * to at most one extra resize.
465 if (numKeysToBeAdded > threshold) {
466 int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
467 if (targetCapacity > MAXIMUM_CAPACITY)
468 targetCapacity = MAXIMUM_CAPACITY;
469 int newCapacity = capacity;
470 while (newCapacity < targetCapacity)
472 if (newCapacity > capacity)
476 for (Iterator<? extends IntHashMap.TEntry> i = m.entrySet().iterator(); i.hasNext(); ) {
477 IntHashMap.TEntry e = i.next();
478 put(e.getHash(), e.getValue());
483 * Removes the mapping for this key from this map if present.
485 * @param key key whose mapping is to be removed from the map.
486 * @return previous value associated with specified key, or <tt>null</tt>
487 * if there was no mapping for key. A <tt>null</tt> return can
488 * also indicate that the map previously associated <tt>null</tt>
489 * with the specified key.
491 public Integer remove(int key) {
492 TEntry e = removeEntryForKey(key);
493 return (e == null ? null : e.getValue());
497 * Removes and returns the entry associated with the specified key
498 * in the HashMap. Returns null if the HashMap contains no mapping
501 TEntry removeEntryForKey(int key) {
502 int hash = hash(key);
503 int i = indexFor(hash, capacity);
504 TEntry prev = table.get(i);
508 TEntry next = e.getNext();
509 if (e.getHash() == hash) {
511 size.decrementAndGet();
516 // e.recordRemoval(this);
527 * Special version of remove for EntrySet.
529 TEntry removeMapping(TEntry o) {
532 int hash = hash(o.getHash());
533 int i = indexFor(hash, capacity);
534 TEntry prev = table.get(i);
538 TEntry next = e.getNext();
539 if (e.getHash() == hash) {
541 size.decrementAndGet();
546 // e.recordRemoval(this);
557 * Removes all mappings from this map.
559 public void clear() {
561 dstm2.AtomicArray<TEntry> tab = table;
562 for (int i = 0; i < capacity; i++)
568 * Returns <tt>true</tt> if this map maps one or more keys to the
571 * @param value value whose presence in this map is to be tested.
572 * @return <tt>true</tt> if this map maps one or more keys to the
575 public boolean containsValue(Object value) {
577 return containsNullValue();
579 dstm2.AtomicArray<TEntry> tab = table;
580 for (int i = 0; i < capacity; i++)
581 for (TEntry e = tab.get(i); e != null ; e = e.getNext())
582 if (value.equals(e.getValue()))
588 * Special-case code for containsValue with null argument
590 private boolean containsNullValue() {
591 dstm2.AtomicArray<TEntry> tab = table;
592 for (int i = 0; i < capacity ; i++)
593 for (TEntry e = tab.get(i) ; e != null ; e = e.getNext())
594 if (e.getValue() == null)
599 @atomic public interface TEntry {
604 void setValue(Integer v);
605 void setNext(TEntry n);
611 * Add a new entry with the specified key, value and hash code to
612 * the specified bucket. It is the responsibility of this
613 * method to resize the table if appropriate.
615 * Subclass overrides this to alter the behavior of put method.
617 void addEntry(int hash, Integer value, int bucketIndex) {
618 TEntry e = table.get(bucketIndex);
619 TEntry n = factory.create();
623 table.set(bucketIndex, n);
624 if (size.incrementAndGet() >= threshold) {
626 if(size.get() >= threshold)
627 resize(2 * capacity);
633 * Like addEntry except that this version is used when creating entries
634 * as part of Map construction or "pseudo-construction" (cloning,
635 * deserialization). This version needn't worry about resizing the table.
637 * Subclass overrides this to alter the behavior of HashMap(Map),
638 * clone, and readObject.
640 void createEntry(int hash, Integer value, int bucketIndex) {
641 TEntry e = table.get(bucketIndex);
642 TEntry n = factory.create();
646 table.set(bucketIndex, n);
647 size.incrementAndGet();
650 private abstract class HashIterator<E> implements Iterator<E> {
651 TEntry next; // next entry to return
652 int expectedModCount; // For fast-fail
653 int index; // current slot
654 TEntry current; // current entry
657 expectedModCount = modCount;
658 dstm2.AtomicArray<TEntry> t = table;
661 if (size.get() != 0) { // advance to first entry
662 while (i > 0 && (n = t.get(--i)) == null)
669 public boolean hasNext() {
674 if (modCount != expectedModCount)
675 throw new ConcurrentModificationException();
678 throw new NoSuchElementException();
680 TEntry n = e.getNext();
681 dstm2.AtomicArray<TEntry> t = table;
683 while (n == null && i > 0)
690 public void remove() {
692 throw new IllegalStateException();
693 if (modCount != expectedModCount)
694 throw new ConcurrentModificationException();
695 int k = current.getHash();
697 IntHashMap.this.removeEntryForKey(k);
698 expectedModCount = modCount;
703 private class ValueIterator extends HashIterator<Integer> {
704 public Integer next() {
705 return nextEntry().getValue();
709 private class KeyIterator extends HashIterator<Integer> {
710 public Integer next() {
711 return nextEntry().getHash();
715 // private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
716 // public Map.Entry<K,V> next() {
717 // return nextEntry();
721 // Subclass overrides these to alter behavior of views' iterator() method
722 public Iterator<Integer> newKeyIterator() {
723 return new KeyIterator();
725 public Iterator<Integer> newValueIterator() {
726 return new ValueIterator();
728 // Iterator<Map.Entry<K,V>> newEntryIterator() {
729 // return new EntryIterator();
735 private transient Set<IntHashMap.TEntry> entrySet = null;
739 private class KeySet extends AbstractSet<Integer> {
740 public Iterator<Integer> iterator() {
741 return newKeyIterator();
746 public boolean contains(Integer o) {
747 return containsKey(o);
749 public boolean remove(Integer o) {
750 return IntHashMap.this.removeEntryForKey(o) != null;
752 public void clear() {
753 IntHashMap.this.clear();
760 * Returns a collection view of the mappings contained in this map. Each
761 * element in the returned collection is a <tt>Map.Entry</tt>. The
762 * collection is backed by the map, so changes to the map are reflected in
763 * the collection, and vice-versa. The collection supports element
764 * removal, which removes the corresponding mapping from the map, via the
765 * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
766 * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
767 * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
769 * @return a collection view of the mappings contained in this map.
772 public Set<IntHashMap.TEntry> entrySet() {
773 Set<IntHashMap.TEntry> es = entrySet;
774 return (es != null ? es : (entrySet = (Set<IntHashMap.TEntry>) (Set) new EntrySet()));
777 private class EntrySet {//extends AbstractSet/*<Map.Entry<K,V>>*/ {
778 // public Iterator/*<Map.Entry<K,V>>*/ iterator() {
779 // return newEntryIterator();
781 public boolean contains(IntHashMap.TEntry o) {
782 IntHashMap.TEntry e = (IntHashMap.TEntry) o;
783 TEntry candidate = getEntry(e.getHash());
784 return candidate != null && candidate.equals(e);
786 public boolean remove(IntHashMap.TEntry o) {
787 return removeMapping(o) != null;
792 public void clear() {
793 IntHashMap.this.clear();
798 * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
801 * @serialData The <i>capacity</i> of the HashMap (the length of the
802 * bucket array) is emitted (int), followed by the
803 * <i>size</i> of the HashMap (the number of key-value
804 * mappings), followed by the key (Object) and value (Object)
805 * for each key-value mapping represented by the HashMap
806 * The key-value mappings are emitted in the order that they
807 * are returned by <tt>entrySet().iterator()</tt>.
810 private void writeObject(java.io.ObjectOutputStream s)
813 Iterator<IntHashMap.TEntry> i = entrySet().iterator();
815 // Write out the threshold, loadfactor, and any hidden stuff
816 s.defaultWriteObject();
818 // Write out number of buckets
819 s.writeInt(capacity);
821 // Write out size (number of Mappings)
822 s.writeInt(size.get());
824 // Write out keys and values (alternating)
825 while (i.hasNext()) {
826 IntHashMap.TEntry e = i.next();
827 s.writeObject(e.getHash());
828 s.writeObject(e.getValue());
832 private static final long serialVersionUID = 362498820763181265L;
835 * Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
838 private void readObject(java.io.ObjectInputStream s)
839 throws IOException, ClassNotFoundException
841 // Read in the threshold, loadfactor, and any hidden stuff
842 s.defaultReadObject();
844 // Read in number of buckets and allocate the bucket array;
845 int numBuckets = s.readInt();
846 table = new dstm2.AtomicArray(TEntry.class, numBuckets);
848 init(); // Give subclass a chance to do its thing.
850 // Read in size (number of Mappings)
851 int size = s.readInt();
853 // Read the keys and values, and put the mappings in the HashMap
854 for (int i=0; i<size; i++) {
855 int key = (Integer) s.readObject();
856 Integer value = (Integer) s.readObject();
857 putForCreate(key, value);
861 // These methods are used when serializing HashSets
862 int capacity() { return capacity; }
863 float loadFactor() { return loadFactor; }
865 public int getCapacity() {
870 public Iterator<Integer> iterator() {
871 return new Iterator<Integer>() {
873 public TEntry cursor = table.get(tableIndex);
874 public boolean hasNext() {
875 return cursor != null;
877 public Integer next() {
878 TEntry node = cursor;
879 cursor = cursor.getNext();
880 while(cursor==null) {
882 if(tableIndex < capacity) {
883 cursor = table.get(tableIndex);
886 return node.getValue();
888 public void remove() {
889 throw new UnsupportedOperationException();