Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。
Hashtable 第一次创建对象的时候就会给底层数组开辟空间 Entry[] 11
HashMap 创建对象时 没有给底层数组进行空间开辟
HashMap把Hashtable的contains方法去掉了。改成containsValue和containsKey
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
Hashtable中,key和value都不允许出现null值
HashMap中,null可以作为键,这样的键只有一个
哈希值的使用不同,HashTable直接使用对象的hashCode
HashMap重新计算hash值。
HashTable在不指定容量的情况下的默认容量为11
HashMap为16
Hashtable不要求底层数组的容量一定要为2的整数次幂
HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1
HashMap扩容时,将容量变为原来的2倍。
Hashtable 中的方法是Synchronize的。
HashMap线程不安全
计算index的方法不同:
像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。
然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引
继承Dictionary 类
是一个抽象类,用来存储键/值对,作用和Map类相似。
给出键和值,你就可以将值存储在Dictionary对象中。一旦该值被存储,就可以通过它的键来获取它。所以和Map一样, Dictionary 也可以作为一个键/值对列表。
Cloneable是标记型的接口,它们内部都没有方法和属性,实现 Cloneable来表示该对象能被克隆
java.io.Serializable接口:
可以启用其序列化功能。未实现次接口的类无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable { /** * The hash table data.哈希表数据 */ private transient Entry<?,?>[] table; /** * The total number of entries in the hash table.哈希表中enyty的总数 */ private transient int count; /** * The table is rehashed when its size exceeds this threshold. (The * value of this field is (int)(capacity * loadFactor).) * 阈值,边界值 * @serial */ private int threshold; /** * The load factor for the hashtable. * 加载因子、负载因子 * @serial */ private float loadFactor; /** * The number of times this Hashtable has been structurally modified * Structural modifications are those that change the number of entries in * the Hashtable or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the Hashtable fail-fast. (See ConcurrentModificationException). */ private transient int modCount = 0; /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = 1421746759512286392L; /** * The maximum size of array to allocate. * 要分配的数组的最大大小。 * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/** * 无参构造,默认容量为11,加载因子为0.75 * Constructs a new, empty hashtable with a default initial capacity (11) * and load factor (0.75). */ public Hashtable() { this(11, 0.75f); }
/** * Constructs a new, empty hashtable with the specified initial capacity * and default load factor (0.75). * 创建指定大小的哈希表 * 初始容量用户自定义,加载因子为默认的0.75 * @param initialCapacity the initial capacity of the hashtable. * @exception IllegalArgumentException if the initial capacity is less * than zero. */ public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); }
/** * Constructs a new, empty hashtable with the specified initial * capacity and the specified load factor. * 创建了一个指定大小的哈希表,并且通过fillRatio指定填充比例 * @param initialCapacity the initial capacity of the hashtable. * @param loadFactor the load factor of the hashtable. * @exception IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive. */ 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); //当初始化容量为0的时候,将初始容量设置为1 if (initialCapacity==0) initialCapacity = 1; //加载因子赋值 this.loadFactor = loadFactor; //创建一个新的大小为初始容量的enytry数组 table = new Entry<?,?>[initialCapacity]; //阈值为initialCapacity * loadFactor或最大值 threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); }
/** * Constructs a new hashtable with the same mappings as the given * Map. The hashtable is created with an initial capacity sufficient to * hold the mappings in the given Map and a default load factor (0.75). * 第四个构造方法创建了一个以M中元素为初始化元素的哈希表。 * 哈希表的容量被设置为M的两倍,或是11 * @param t the map whose mappings are to be placed in this map. * @throws NullPointerException if the specified map is null. * @since 1.2 */ public Hashtable(Map<? extends K, ? extends V> t) { this(Math.max(2*t.size(), 11), 0.75f); putAll(t); }
/** * Tests if some key maps into the specified value in this hashtable. * This operation is more expensive than the {@link #containsKey * containsKey} method. * 测试此映射表中是否存在与指定值关联的键。 * <p>Note that this method is identical in functionality to * {@link #containsValue containsValue}, (which is part of the * {@link Map} interface in the collections framework). * * @param value a value to search for * @return <code>true</code> if and only if some key maps to the * <code>value</code> argument in this hashtable as * determined by the <tt>equals</tt> method; * <code>false</code> otherwise. * @exception NullPointerException if the value is <code>null</code> */ public synchronized boolean contains(Object value) { //如果传入的value值为空,则抛出异常 if (value == null) { throw new NullPointerException(); } //2层循环遍历数组及其链表 Entry<?,?> tab[] = table; for (int i = tab.length ; i-- > 0 ;) { for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) { if (e.value.equals(value)) { return true; } } } return false; }
/** * 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. * 将指定 key 映射到此哈希表中的指定 value。 * @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 Object#equals(Object) * @see #get(Object) */ 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]; //当该位置已有元素,则发生哈希冲突,遍历链表找通过equal方法找到key for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { //覆盖原来的值 V old = entry.value; entry.value = value; //返回原来的值 return old; } }
private void addEntry(int hash, K key, V value, int index) { modCount++; Entry<?,?> tab[] = table; //如果数组有效值等于阈值,将进行扩容操作,然后在计算新的index 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.创建新条目。count++ @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; }
/** * Increases the capacity of and internally reorganizes this * hashtable,增加hashtable的容量并对其重组 * in order to accommodate and access its entries more * efficiently. * 以便高效地访问更多的条目 * This method is called automatically when the number of keys in the hashtable exceeds this hashtable's capacity and load factor. * 当哈希表中的键数超过该哈希表的容量和负载因子时,将自动调用此方法。 */ @SuppressWarnings("unchecked") protected void rehash() { //先将数组原来的容量保存起来 int oldCapacity = table.length; Entry<?,?>[] oldMap = table; // overflow-conscious code //新的容量为原来的一半 + 1 int newCapacity = (oldCapacity << 1) + 1; //如果新的容量超出了最大值,则将新容量变为最大值 if (newCapacity - MAX_ARRAY_SIZE > 0) { //如果原来的容量就是最大值则将不扩容,return if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } //新建一个新容量大小的数组 Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++; //阈值也重新赋值,如若已经超出最大值,则为MAX_ARRAY_SIZE + 1 threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); //将table指向新数组 table = newMap; //遍历原来的hashtable,将其拷贝过来 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; //数组下标值不同于hashmap,都重新计算 int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry<K,V>)newMap[index]; newMap[index] = e; } } }
synchronized 关键字