本文的目的并不是让你对 一、 类定义public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable 可以看到它和 /** * Maps the specified <code>key</code> to the specified * <code>value</code> in this dictionary. Neither the key nor the * value can be <code>null</code>. */abstract public V put(K key, V value);abstract public Enumeration<K> keys();abstract public Enumeration<V> elements();public interface Enumeration<E> { boolean hasMoreElements(); E nextElement(); } 同
二、构造方法和成员变量private transient Entry<?,?>[] table; // 哈希槽private int threshold; // 阈值private float loadFactor; // 负载系数 以上三个应该就是 Map 中最重要的成员变量了,阈值和负载系数控制扩容时机,同 public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: " initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: " loadFactor); if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry<?,?>[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE 1); }public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); }public Hashtable() { this(11, 0.75f); }public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); } 如代码所示四个构造函数,主要就是为了初始化以上三个成员变量,但是注意
三、重要方法1. 哈希槽定位int index = (hash & 0x7FFFFFFF) % tab.length; 哈希表中最重要的方法肯定是哈希槽定位,如上面的原因 2. get 方法public synchronized V get(Object key) { Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; } 注意
private static class A { String name; public A(String name) {this.name = name;} @Override public boolean equals(Object o) { ... } @Override public int hashCode() { ... } }private static void test01() { Map<A, String> map = new Hashtable<>(); A a1 = new A("a"); A a2 = new A("a"); map.put(a1, "a"); map.put(a2, "a"); System.out.println(map.get(a1)); a1.name = "b"; System.out.println(map.get(a1)); } // 打印: 3. put 方法public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; }
4. addEntry 方法private void addEntry(int hash, K key, V value, int index) { modCount ; Entry<?,?> tab[] = table; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count ; }private static class Entry<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } ... } 这里添加元素的时候首先判断是否扩容,然后添加节点;值得注意的是添加的节点是直接放在哈希槽里的( 5. rehash 方法protected void rehash() { int oldCapacity = table.length; Entry<?,?>[] oldMap = table; // overflow-conscious code int newCapacity = (oldCapacity << 1) 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount ; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry<K,V>)newMap[index]; newMap[index] = e; } } } 扩容的时候也是,先计算新容量,在得到一个新的哈希槽,然后将节点在依次放入;同添加节点一样是将节点直接放到哈希槽中,那么在扩容完毕之后,链表的相对顺序会反向; 总结
|
|