📄 hashtable.java
字号:
* Returns true if this Hashtable equals the supplied Object <code>o</code>. * As specified by Map, this is: * <code> * (o instanceof Map) && entrySet().equals(((Map) o).entrySet()); * </code> * * @param o the object to compare to * @return true if o is an equal map * @since 1.2 */ public boolean equals(Object o) { // no need to synchronize, entrySet().equals() does that if (o == this) return true; if (!(o instanceof Map)) return false; return entrySet().equals(((Map) o).entrySet()); } /** * Returns the hashCode for this Hashtable. As specified by Map, this is * the sum of the hashCodes of all of its Map.Entry objects * * @return the sum of the hashcodes of the entries * @since 1.2 */ public synchronized int hashCode() { // Since we are already synchronized, and entrySet().iterator() // would repeatedly re-lock/release the monitor, we directly use the // unsynchronized HashIterator instead. Iterator itr = new HashIterator(ENTRIES); int hashcode = 0; for (int pos = size; pos > 0; pos--) hashcode += itr.next().hashCode(); return hashcode; } /** * Helper method that returns an index in the buckets array for `key' * based on its hashCode(). * * @param key the key * @return the bucket number * @throws NullPointerException if key is null */ private int hash(Object key) { // Note: Inline Math.abs here, for less method overhead, and to avoid // a bootstrap dependency, since Math relies on native methods. int hash = key.hashCode() % buckets.length; return hash < 0 ? -hash : hash; } /** * Helper method for entrySet(), which matches both key and value * simultaneously. Ignores null, as mentioned in entrySet(). * * @param o the entry to match * @return the matching entry, if found, or null * @see #entrySet() */ // Package visible, for use in nested classes. HashEntry getEntry(Object o) { if (! (o instanceof Map.Entry)) return null; Object key = ((Map.Entry) o).getKey(); if (key == null) return null; int idx = hash(key); HashEntry e = buckets[idx]; while (e != null) { if (e.equals(o)) return e; e = e.next; } return null; } /** * A simplified, more efficient internal implementation of putAll(). clone() * should not call putAll or put, in order to be compatible with the JDK * implementation with respect to subclasses. * * @param m the map to initialize this from */ void putAllInternal(Map m) { Iterator itr = m.entrySet().iterator(); size = 0; while (itr.hasNext()) { size++; Map.Entry e = (Map.Entry) itr.next(); Object key = e.getKey(); int idx = hash(key); HashEntry he = new HashEntry(key, e.getValue()); he.next = buckets[idx]; buckets[idx] = he; } } /** * Increases the size of the Hashtable and rehashes all keys to new array * indices; this is called when the addition of a new value would cause * size() > threshold. Note that the existing Entry objects are reused in * the new hash table. * <p> * * This is not specified, but the new size is twice the current size plus * one; this number is not always prime, unfortunately. This implementation * is not synchronized, as it is only invoked from synchronized methods. */ protected void rehash() { HashEntry[] oldBuckets = buckets; int newcapacity = (buckets.length * 2) + 1; threshold = (int) (newcapacity * loadFactor); buckets = new HashEntry[newcapacity]; for (int i = oldBuckets.length - 1; i >= 0; i--) { HashEntry e = oldBuckets[i]; while (e != null) { int idx = hash(e.key); HashEntry dest = buckets[idx]; if (dest != null) { while (dest.next != null) dest = dest.next; dest.next = e; } else { buckets[idx] = e; } HashEntry next = e.next; e.next = null; e = next; } } } /** * Serializes this object to the given stream. * * @param s the stream to write to * @throws IOException if the underlying stream fails * @serialData the <i>capacity</i> (int) that is the length of the * bucket array, the <i>size</i> (int) of the hash map * are emitted first. They are followed by size entries, * each consisting of a key (Object) and a value (Object). */ private synchronized void writeObject(ObjectOutputStream s) throws IOException { // Write the threshold and loadFactor fields. s.defaultWriteObject(); s.writeInt(buckets.length); s.writeInt(size); // Since we are already synchronized, and entrySet().iterator() // would repeatedly re-lock/release the monitor, we directly use the // unsynchronized HashIterator instead. Iterator it = new HashIterator(ENTRIES); while (it.hasNext()) { HashEntry entry = (HashEntry) it.next(); s.writeObject(entry.key); s.writeObject(entry.value); } } /** * Deserializes this object from the given stream. * * @param s the stream to read from * @throws ClassNotFoundException if the underlying stream fails * @throws IOException if the underlying stream fails * @serialData the <i>capacity</i> (int) that is the length of the * bucket array, the <i>size</i> (int) of the hash map * are emitted first. They are followed by size entries, * each consisting of a key (Object) and a value (Object). */ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { // Read the threshold and loadFactor fields. s.defaultReadObject(); // Read and use capacity. buckets = new HashEntry[s.readInt()]; int len = s.readInt(); // Read and use key/value pairs. // TODO: should we be defensive programmers, and check for illegal nulls? while (--len >= 0) put(s.readObject(), s.readObject()); } /** * A class which implements the Iterator interface and is used for * iterating over Hashtables. * This implementation is parameterized to give a sequential view of * keys, values, or entries; it also allows the removal of elements, * as per the Javasoft spec. Note that it is not synchronized; this is * a performance enhancer since it is never exposed externally and is * only used within synchronized blocks above. * * @author Jon Zeppieri */ private final class HashIterator implements Iterator { /** * The type of this Iterator: {@link #KEYS}, {@link #VALUES}, * or {@link #ENTRIES}. */ final int type; /** * The number of modifications to the backing Hashtable that we know about. */ int knownMod = modCount; /** The number of elements remaining to be returned by next(). */ int count = size; /** Current index in the physical hash table. */ int idx = buckets.length; /** The last Entry returned by a next() call. */ HashEntry last; /** * The next entry that should be returned by next(). It is set to something * if we're iterating through a bucket that contains multiple linked * entries. It is null if next() needs to find a new bucket. */ HashEntry next; /** * Construct a new HashIterator with the supplied type. * @param type {@link #KEYS}, {@link #VALUES}, or {@link #ENTRIES} */ HashIterator(int type) { this.type = type; } /** * Returns true if the Iterator has more elements. * @return true if there are more elements * @throws ConcurrentModificationException if the hashtable was modified */ public boolean hasNext() { if (knownMod != modCount) throw new ConcurrentModificationException(); return count > 0; } /** * Returns the next element in the Iterator's sequential view. * @return the next element * @throws ConcurrentModificationException if the hashtable was modified * @throws NoSuchElementException if there is none */ public Object next() { if (knownMod != modCount) throw new ConcurrentModificationException(); if (count == 0) throw new NoSuchElementException(); count--; HashEntry e = next; while (e == null) e = buckets[--idx]; next = e.next; last = e; if (type == VALUES) return e.value; if (type == KEYS) return e.key; return e; } /** * Removes from the backing Hashtable the last element which was fetched * with the <code>next()</code> method. * @throws ConcurrentModificationException if the hashtable was modified * @throws IllegalStateException if called when there is no last element */ public void remove() { if (knownMod != modCount) throw new ConcurrentModificationException(); if (last == null) throw new IllegalStateException(); Hashtable.this.remove(last.key); last = null; knownMod++; } } // class HashIterator /** * Enumeration view of this Hashtable, providing sequential access to its * elements; this implementation is parameterized to provide access either * to the keys or to the values in the Hashtable. * * <b>NOTE</b>: Enumeration is not safe if new elements are put in the table * as this could cause a rehash and we'd completely lose our place. Even * without a rehash, it is undetermined if a new element added would * appear in the enumeration. The spec says nothing about this, but * the "Java Class Libraries" book infers that modifications to the * hashtable during enumeration causes indeterminate results. Don't do it! * * @author Jon Zeppieri */ private final class Enumerator implements Enumeration { /** * The type of this Iterator: {@link #KEYS} or {@link #VALUES}. */ final int type; /** The number of elements remaining to be returned by next(). */ int count = size; /** Current index in the physical hash table. */ int idx = buckets.length; /** * Entry which will be returned by the next nextElement() call. It is * set if we are iterating through a bucket with multiple entries, or null * if we must look in the next bucket. */ HashEntry next; /** * Construct the enumeration. * @param type either {@link #KEYS} or {@link #VALUES}. */ Enumerator(int type) { this.type = type; } /** * Checks whether more elements remain in the enumeration. * @return true if nextElement() will not fail. */ public boolean hasMoreElements() { return count > 0; } /** * Returns the next element. * @return the next element * @throws NoSuchElementException if there is none. */ public Object nextElement() { if (count == 0) throw new NoSuchElementException("Hashtable Enumerator"); count--; HashEntry e = next; while (e == null) e = buckets[--idx]; next = e.next; return type == VALUES ? e.value : e.key; } } // class Enumerator} // class Hashtable
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -