hashtable 的定义:
public class HashTable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable继承自Dictionary,Dictionary是一个抽象父类,功能和Map一样,但过时了,官方推荐用实现Map接口来取代。
并且实现了Map接口,以及Cloneable,Serializable接口。
和hashMap的区别:
null值问题
HashTable键(key)和值(value)均不能为null。
/** * 将key和value加入到map中,明显标明, * value不能为null。如果key为null,则会报nullPointer异常 */ 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(); //很直接的利用hashcode去除table.length,然后取长度。 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)) { //hash相同且equals,那么就连在后面,是用链表的方式。 V old = entry.value; entry.value = value; return old; } } //第一个,链表后面没有数据。 addEntry(hash, key, value, index); return null; }关于value,明显有if判断,不能为null,
如果key为null,则也直接在计算hashCode的时候就会报空指针。
计算table数组索引值方法
int hash = key.hashCode(); //直接用hashcode % n int index = (hash & 0x7FFFFFFF) % tab.length;
而hashmap int index=e.hash % n ;
hash值的计算方式不同! 很直接的利用hashcode去除table.length,然后取长度。
HashTable是线程安全
因为HashTable中大部分方法都是加了synchronized关键字,所以同一时刻,只能有一个线程进入其方法故是线程安全的。
initialCapacity和loadFactor
HashMap中:initialCapacity=16,loadFactory=0.75HashTable中:initialCapacity=11,loadFactory=0.75
只有链表方式解决冲突
Java8,HashMap中,当出现冲突时,
- 如果冲突数量小于8,则是以链表方式解决冲突。
- 而当冲突大于等于8时,就会将冲突的Entry转换为红黑树进行存储。
- 而又当数量小于6时,则又转化为链表存储。
而在HashTable中, 则都是以链表方式存储。
扩容的额度
Java8中, HashMap:
一旦扩容,都是扩展到2的倍数,因为这样有利于计算数组索引值,即和计算数组索引结合起来。
HashTable: 一次性扩展为oldCapacity*2+1。
/** * 一次扩展是,old*2+1 */ @SuppressWarnings("unchecked") 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值。取newCapacity*loadFactor的小值。 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; } } }先扩展,再把旧数组里面元素一个一个加到新的里面。
注意, 这里取hash不是e.hash,而仍然是key.hashCode计算保留下来的值。
参考:
https://blog.csdn.net/anla_/article/details/78298484
https://blog.csdn.net/zldeng19840111/article/details/6703104