引言
Hashtable的淘汰开始于它的“t”没有大写,hh~。
Hashtable 是Java中第一批用来实现hash的数据结构,但是长江后浪推前浪,Hashtable逐渐退出舞台,本文就HashMap和Hashtable的差异进行比较和总结。
差异比较
一、hash函数差异以及 K,V 参数要求
Hash数据结构的第一个关键是hash函数实现,即hash值的获取办法,差异如下:
HashMap:
//Map的put方法实际上调用了putVal方法,有单独的hash函数方法;
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//Map的hash函数
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Hashtable:
//value为空,抛出空指针异常
if (value == null) {
throw new NullPointerException();
}
Entry<?,?> tab[] = table;
//hash值则是直接调用hashcode方法获得
int hash = key.hashCode();
//index索引取正之后再取余;
int index = (hash & 0x7FFFFFFF) % tab.length;
通过源码,我们可以看见,Hashtable的K和V是不能为空的,而HashMap则考虑了K、V为空的情况。
其中,K为空,则hash值置0,value为空,Map照样放进去;
二、synchronized差异
看Hashtable 里面的函数,我们会发现,函数声明时都会带有一个synchronized关键字,而HashMap没有。
这个会有什么影响呢?
我们知道,synchronized关键字是用来线程间进行同步的,即当多个线程想要对同一个Hashtable操作时,只有一个可以获得权限,而其他的得等到该线程运行完毕才能对Hashtable操作。毫无疑问,对于单线程程序来说,这肯定会降低运行的速度。但是,对于HashMap来说,他就不是多线程安全的。
于是在这个点上,HashMap提供两个方法弥补这个缺陷:
- 使用ConcurrentHashMap;
这个方法在规模和速度上都来的比Hashtable优秀; - 利用 Map m = Collections.synchronizeMap(hashMap) 实现synchronized;
三、Enumerator 与 Iterator
HashMap 的 iterator是fail-fast机制的,而Hashtable的enumerator不是。
The iterators returned by all of this class’s “collection view methods” are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException.
fail-fast机制: 创建iterator之后,如果不是调用iterator的remove方法,任何改变HashMap结构的操作都会导致抛出ConcurrentModificationException。
这样做的意义在于:如果有多线程修改HashMap,iterator能够快速发现,而不会去做一些”冒险“的事情。
但是,要注意的是,fail-fast机制并不一定是同步多线程才能触发的,单线程也行,如下代码所示:
public static void main(String[] args) {
LinkedList<String> ll= new LinkedList<String>();
ll.add("1");
ll.add("2");
for(Iterator<String> iterator = ll.iterator();iterator.hasNext();){
String content = iterator.next();
System.out.println(content);
if(content.equals("2"))
ll.add("3");
}
}
output:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(Unknown Source)
at java.util.LinkedList$ListItr.next(Unknown Source)
at test.A.main(A.java:13)
所以,从这点上来看,fail-fast只是给了我们一种检查bug的方法,但利用这个机制来确保程序的正确性不是一个好的选择。
四、HashMap的注意事项
HashMap add时的顺序和输出时的顺序不一样。hash内部的排序机制就是如此,在存储时就是根据hash获得索引位置,本身即是乱序,如果想要add顺序和输出顺序一致,可以使用LinkedHashMap结构,里面维护了一个双向链表来实现维持顺序;
结语
Hashtable已经逐渐被淘汰,虽然还在更新,但也只是做特殊使用,官方文档也提醒着大家尽量使用HashMap优化程序,同时也提供了相应的thread-safe方法来弥补HashMap的缺点。
Code路漫漫兮其修远,吾将上下而求索。
本文持续更新中。。。。