HashMap是线程不安全的,在多线程环境下会容易产生死循环,但是单线程环境下运行效率高;Hashtable线程安全的,很多方法都有synchronized修饰,但同时因为加锁导致单线程环境下效率较低。
HashMap允许有一个key为null,允许多个value为null;而Hashtable不允许key或者value为null。
构造函数的比较:
HashMap:
1 | public HashMap(int initialCapacity, float loadFactor) { |
HashTable:
1 | public Hashtable(int initialCapacity, float loadFactor) { |
可以看出HashMap的底层数组的长度必须为2^n,这样做的好处是为以后的hash算法做准备,而Hashtable底层数组的长度可以为任意值,这就造成了当底层数组长度为合数的时候,Hashtable的hash算法散射不均匀,容易产生hash冲突。所以,可以清楚的看到Hashtable的默认构造函数底层数组长度为11(质数),至于为什么Hashtable的底层数组用质数较好,请参考博文:http://blog.csdn.net/liuqiyao_01/article/details/14475159;
HashMap扩容:
1 | final Node<K,V>[] resize() { |
从源码中可以看出,HashMap数组的扩容的整体思想就是创建一个长度为原先2倍的数组。然后对原数组进行遍历和复制。只不过jdk1.8对扩容进行优化,使得扩容不再需要进行链表的反转,只需要知道hashcode新增的bit位为0还是1。如果是0就在原索引位置,新增索引是1就在oldIndex+oldCap位置。
HashTable扩容:
1 | protected void rehash() { |
Hashtable的扩容将先创建一个长度为原长度2倍的数组,再使用头插法将链表进行反序。
总结
可以看出到jdk1.8 HashMap和Hashtable的区别越来越大,HashMap相较与之前的jdk做了很多的优化,最重要的是在内部实现结构上引进了红黑数还有扩容上的优化。Hashtable作为jdk1.2遗留下来的类,到jdk1.8没有大改,所以对数据的一致性要求较低的话可以使用ConcurrentHashMap来替代Hashtable。