/** * Associate the specified value with the specified key in this {@code Hashtable}. If the key * already exists, the old value is replaced. The key and value cannot be null. * * @param key the key to add. * @param value the value to add. * @return the old value associated with the specified key, or {@code null} if the key did not * exist. * @see #elements * @see #get * @see #keys * @see java.lang.Object#equals */ public synchronized V put(K key, V value) { if (key == null) { throw new NullPointerException("key == null"); } else if (value == null) { throw new NullPointerException("value == null"); } int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); HashtableEntry<K, V> first = tab[index]; for (HashtableEntry<K, V> e = first; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { V oldValue = e.value; e.value = value; return oldValue; } } // No entry for key is present; create one modCount++; if (size++ > threshold) { rehash(); // Does nothing!! tab = doubleCapacity(); index = hash & (tab.length - 1); first = tab[index]; } tab[index] = new HashtableEntry<K, V>(key, value, hash, first); return null; }
/** * Ensures that the hash table has sufficient capacity to store the specified number of mappings, * with room to grow. If not, it increases the capacity as appropriate. Like doubleCapacity, this * method moves existing entries to new buckets as appropriate. Unlike doubleCapacity, this method * can grow the table by factors of 2^n for n > 1. Hopefully, a single call to this method will be * faster than multiple calls to doubleCapacity. * * <p>This method is called only by putAll. */ private void ensureCapacity(int numMappings) { int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings)); HashtableEntry<K, V>[] oldTable = table; int oldCapacity = oldTable.length; if (newCapacity <= oldCapacity) { return; } rehash(); // Does nothing!! if (newCapacity == oldCapacity * 2) { doubleCapacity(); return; } // We're growing by at least 4x, rehash in the obvious way HashtableEntry<K, V>[] newTable = makeTable(newCapacity); if (size != 0) { int newMask = newCapacity - 1; for (int i = 0; i < oldCapacity; i++) { for (HashtableEntry<K, V> e = oldTable[i]; e != null; ) { HashtableEntry<K, V> oldNext = e.next; int newIndex = e.hash & newMask; HashtableEntry<K, V> newNext = newTable[newIndex]; newTable[newIndex] = e; e.next = newNext; e = oldNext; } } } }
/** * Maps the specified <code>key</code> to the specified <code>value</code> in this hashtable. * Neither the key nor the value can be <code>null</code>. * * <p>The value can be retrieved by calling the <code>get</code> method with a key that is equal * to the original key. * * @param key the hashtable key. * @param value the value. * @return the previous value of the specified key in this hashtable, or <code>null</code> if it * did not have one. * @exception NullPointerException if the key or value is <code>null</code>. * @see java.lang.Object#equals(java.lang.Object) * @see java.util.Hashtable#get(java.lang.Object) * @since JDK1.0 */ public Object put(Object key, Object value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. HashtableEntry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (HashtableEntry e = tab[index]; e != null; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { Object old = e.value; e.value = value; return old; } } if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); return put(key, value); } // Creates the new entry. HashtableEntry e = new HashtableEntry(); e.hash = hash; e.key = key; e.value = value; e.next = tab[index]; tab[index] = e; count++; return null; }
/** * Removes the key (and its corresponding value) from this hashtable. This method does nothing if * the key is not in the hashtable. * * @param key the key that needs to be removed. * @return the value to which the key had been mapped in this hashtable, or <code>null</code> if * the key did not have a mapping. * @since JDK1.0 */ public Object remove(Object key) { HashtableEntry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; // this loop was reviewed - code is approved! for (HashtableEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; return e.value; } } return null; }
/** * Removes the key/value pair with the specified key from this {@code Hashtable}. * * @param key the key to remove. * @return the value associated with the specified key, or {@code null} if the specified key did * not exist. * @see #get * @see #put */ public synchronized V remove(Object key) { int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashtableEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && key.equals(e.key)) { if (prev == null) { tab[index] = e.next; } else { prev.next = e.next; } modCount++; size--; return e.value; } } return null; }
protected void rehash() { int oldCapacity = table.length; HashtableEntry oldTable[] = table; int newCapacity = oldCapacity * 2 + 1; HashtableEntry newTable[] = new HashtableEntry[newCapacity]; threshold = (int) (newCapacity * loadFactor); table = newTable; for (int i = oldCapacity; i-- > 0; ) { for (HashtableEntry old = oldTable[i]; old != null; ) { HashtableEntry e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = newTable[index]; newTable[index] = e; } } }
/** * Removes the mapping from key to value and returns true if this mapping exists; otherwise, * returns does nothing and returns false. */ private synchronized boolean removeMapping(Object key, Object value) { int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); for (HashtableEntry<K, V> e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && e.key.equals(key)) { if (!e.value.equals(value)) { return false; // Map has wrong value for key } if (prev == null) { tab[index] = e.next; } else { prev.next = e.next; } modCount++; size--; return true; } } return false; // No entry for key }
/** * This method is just like put, except that it doesn't do things that are inappropriate or * unnecessary for constructors and pseudo-constructors (i.e., clone, readObject). In particular, * this method does not check to ensure that capacity is sufficient, and does not increment * modCount. */ private void constructorPut(K key, V value) { if (key == null) { throw new NullPointerException("key == null"); } else if (value == null) { throw new NullPointerException("value == null"); } int hash = secondaryHash(key.hashCode()); HashtableEntry<K, V>[] tab = table; int index = hash & (tab.length - 1); HashtableEntry<K, V> first = tab[index]; for (HashtableEntry<K, V> e = first; e != null; e = e.next) { if (e.hash == hash && key.equals(e.key)) { e.value = value; return; } } // No entry for key is present; create one tab[index] = new HashtableEntry<K, V>(key, value, hash, first); size++; }
protected Object clone() { HashtableEntry entry = new HashtableEntry(); entry.hash = hash; entry.key = key; entry.value = value; entry.next = (next != null) ? (HashtableEntry) next.clone() : null; return entry; }
/** * Removes the key and its corresponding value. Key is searched case insensitively. * * @param key string key * @return object removed or null if none * @exception IllegalArgumentException if key is not s string */ public synchronized Object remove(Object key) throws IllegalArgumentException { if (table == null) return null; try { HashtableEntry tab[] = table; int hash = hashCode((String) key); int index = (hash & 0x7FFFFFFF) % tab.length; for (HashtableEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) { if ((e.hash == hash) && e.key.equalsIgnoreCase((String) key)) { if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; return e.value; } } return null; } catch (ClassCastException cce) { throw new IllegalArgumentException("Non string keys are not accepted!"); } }
/** * Doubles the capacity of the hash table. Existing entries are placed in the correct bucket on * the enlarged table. If the current capacity is, MAXIMUM_CAPACITY, this method is a no-op. * Returns the table, which will be new unless we were already at MAXIMUM_CAPACITY. */ private HashtableEntry<K, V>[] doubleCapacity() { HashtableEntry<K, V>[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { return oldTable; } int newCapacity = oldCapacity * 2; HashtableEntry<K, V>[] newTable = makeTable(newCapacity); if (size == 0) { return newTable; } for (int j = 0; j < oldCapacity; j++) { /* * Rehash the bucket using the minimum number of field writes. * This is the most subtle and delicate code in the class. */ HashtableEntry<K, V> e = oldTable[j]; if (e == null) { continue; } int highBit = e.hash & oldCapacity; HashtableEntry<K, V> broken = null; newTable[j | highBit] = e; for (HashtableEntry<K, V> n = e.next; n != null; e = n, n = n.next) { int nextHighBit = n.hash & oldCapacity; if (nextHighBit != highBit) { if (broken == null) newTable[j | nextHighBit] = n; else broken.next = n; broken = e; highBit = nextHighBit; } } if (broken != null) broken.next = null; } return newTable; }
/** * Puts the key and the value in the table. If there already is a key equal ignore case to the one * passed the new value exchhanes the old one. * * @param key String key * @param value object to put * @return old value if any, or null if none * @exception IllegalArgumentException if key is not a string */ public synchronized Object put(Object key, Object value) throws IllegalArgumentException { if (value == null) { throw new NullPointerException(); } if (table == null) initTable(MIN_CAPACITY); try { // Makes sure the key is not already in the hashtable. int hash = hashCode((String) key); int index; HashtableEntry[] tab = null; do { tab = table; index = (hash & 0x7FFFFFFF) % tab.length; for (HashtableEntry e = tab[index]; e != null; e = e.next) { if ((e.hash == hash) && e.key.equalsIgnoreCase((String) key)) { Object old = e.value; e.value = value; return old; } } if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); continue; } break; } while (true); // Creates the new entry. HashtableEntry e = new HashtableEntry(); e.hash = hash; e.key = (String) key; e.value = value; e.next = tab[index]; tab[index] = e; count++; return null; } catch (ClassCastException cce) { throw new IllegalArgumentException("Non string keys are not accepted!"); } }