集合源码学习(十):HashTable(Java8)与HashMap比较
2017-10-20 19:02
686 查看
什么是HashTable
一句话介绍,一个里面方法大部分都是线程安全的集合,类似于HashMap。也是通过一个数组,利用hash函数,如果冲突就用链表进行连接。具体如下图:
当然这只是一句话简短介绍,面试的时候,经常会被问到,
HashTable和HashMap有什么区别?
经常的回答就是,
HashTable是线程安全,里面方法大部分是synchronized,而HashMap不是;
HashMap里面key和value可以为null,而HashTable中不允许为null;
在没细致分析集合源码之前,我也是这样认为的,在面试中也是这样回答的(逃
看完两个源码后,发现自己了解的真实太浅了。
有兴趣可以看我前一篇分析HashMap的文章:
集合源码学习(七):HashMap(Java8)
首先看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。先看put方法:
/** * 将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数组索引值方法
相信大家应该还记得,java8中HashMap更改了hash()方法:HashMap中:
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
接着计算数组索引:
tab[i = (n - 1) & hash]
而在HashTable中:
int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length;
很直接的利用hashcode去除table.length,然后取长度。
HashTable是线程安全
因为HashTable中大部分方法都是加了synchronized关键字,所以同一时刻,只能有一个线程进入其方法故是线程安全的,这里就不多说。initialCapacity和loadFactor
有些读者可能还不懂这两个参数的意思,initialCapacity是初始容量,而loadFactor是加载因子,当当前已用大小size=table.length*loadFactory时,就会进行扩容操作。具体可以看我的HashMap分析篇。说重点,没有指定初始容量时
HashMap中:initialCapacity=16,loadFactory=0.75
HashTable中:initialCapacity=11,loadFactory=0.75
只有链表方式解决冲突
Java8,HashMap中,当出现冲突时,如果冲突数量小于8,则是以链表方式解决冲突。
而当冲突大于等于8时,就会将冲突的Entry转换为红黑树进行存储。
而又当数量小于6时,则又转化为链表存储。
而在HashTable中,
则都是以链表方式存储。
扩容的额度
Java8中,HashMap:
HashMap一旦扩容,都是扩展到2的倍数,因为这样有利于计算数组索引值,即和计算数组索引结合起来。就算指定大小,也是会选择一个大于大小的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计算保留下来的值。
如果哪里分析有问题或者哪些地方没有分析到的,可以在下方留言。
另附上:
集合源码学习(七):HashMap(Java8)
相关文章推荐
- Java集合源码学习笔记(五)ArrayList,LinkedList,Vector和Hashtable,HashMap的比较
- JDK源码学习之集合HashMap、Hashtable、WeakHashMap比较
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- JAVA学习18_Java集合---HashMap源码剖析
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java 8集合框架源码学习——HashMap
- 集合源码学习(九):WeakHashMap(Java8)关于key
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java集合源码学习(24)_ConcurrentMap的实现类ConcurrentHashMap
- 集合源码学习(七):HashMap(Java8)
- Java集合(17)--HashMap、Hashtable和ConcurrentHashMap的比较
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java集合ArrayList和HashMap源码学习
- java—HashMap与Hashtable的源码比较
- JAVA学习笔记(二十二)- 集合HashMap与Hashtable
- java集合(ArrayList、vector、HashMap、HashTable)源码剖析
- 【Java集合学习系列】HashMap实现原理及源码分析