Hashtable和HashMap

前不久用JAVA写了一个简单的哈希表,但是在和Hashtable、HashMap进行性能测试比较时,发现相差得有点大,于是专门去探究了一下Hashtable和HashMap的一些特性。

一、Hashtable和HashMap的不同

1.继承对象不同

Hashtable继承Dictionary类,而HashMap继承Map类继承AbstractMap类。

2.哈希表的初始容量和扩展方法不同

Hashtable的默认初始容量是11。而HashMap的默认初始容量是16。并且它的注释还强调了一句话,“The default initial capacity - MUST be a power of two.”就是这个初始容量的大小必须是2的倍数,这个强调是为了后面用移位操作来取代一些除法操作。因为在计算机中除法时非常耗时间的,而移位操作相对简单的多。

Hashtable的扩展公式为2*n+1,而HashMap的扩展公式为2*n。Hashtable的扩展方式使得容量大小一直都是奇数,因为只有奇数才有可能是素数,它希望每次扩展后的容量都能够是素数,这样子就能够减少哈希冲突次数。而HashMap则是为了简化一些计算,把取余数的除法都变成简单的位移操作,因此扩展后的哈希表容量都是2的倍数。

3.哈希码的计算方法不同——决定添加数据是否可为null

Hashtable的源代码如下。它的put()方法中明确指出如果value等于null就会抛出空指针异常的错误。但是这里并没有说key值不能为null,于是我就尝试着添加一个key为null,value不为null的值,结果还是抛出了空指针的错误。

既然put函数中没有指明key必须为null,那就是put()方法里面调用的其他方法有这个限制,初步猜测应该是hashcode()方法,但是这个方法是private类型的,我们在这里看不到它的具体实现。那么就只能进行手动测试了。测过结果如下:

果然是hashcode()的问题。null是没有hashcode的。

我们接着来看一下HashMap。它的put方法中没有对value进行限制,那么key呢?既然Object类里面的Hashcode()不允许为null,如果HashMap直接调用这个函数来计算key的哈希码,那么它的key也肯定不能为null。也许正是为了让key可以为null,他们并没有直接使用Object自带的hashcode()方法,而是自己写了一个hash方法来计算key的哈希码,不过其中还是调用了hashcode()方法,代码如下。如果key==null,那么它的哈希码就为0;如果不为null,再调用hashcode()方法,并进行一个异或运算。这样子HashMap添加的数据key和value都可以为null。

注意:判断是否含有某个键
在HashMap 中,null 可以作为键。当get()方法返回null 值时,既可以表示HashMap 中没有该键,也可以表示该键所对应的值为null。因此,在HashMap 中不能用get()方法来判断HashMap 中是否存在某个键,而应该用containsKey()方法来判断。

Hashtable 的键值都不能为null,所以可以用get()方法来判断是否含有某个键。返回null的时候说明没有表中这个键值对。

4.遍历方式不同

Hashtable是使用的是Enumeration(枚举),而HashMap使用iterator(迭代器迭代)。

5.是否线程安全的

Hashtable是线程安全的,它使用了syncronized关键字来保证线程安全(所谓线程安全就是多个线程都在使用这个哈希表时,不会出现数据混乱),而Hashtable的线程安全采用的是最简单粗暴的方法——如果当前已经有一个线程在使用这个哈希表,那么这个哈希表就会被锁住,不允许其他线程使用,其他线程只能等这个线程使用结束后才能使用,这样一来虽然操作安全,但是也会降低程序的效率。而HashMap不是线程安全的。它允许多个线程同时操作同一个哈希表,这样子效率虽然高,但是哈希表的结构和数据可能会乱掉。因此多线程编程优先选择Hashtable,单线程编程优先选择HashMap。

当然还有一种更好的选择,那就是ConcurrentHashMap,这个继承自AbstractMap,跟Map很相似。但是它进一步处理HashMap线程不安全的问题。它跟Hashtable一样是线程安全的,而相较于Hashtable的整段锁,它采用的是分段锁的方法,提高了程序的执行效率。

二、两者的相同点

1.它们的装载因子都是0.75。

2.都是采用数组+链表的实现

三、如何提高哈希表的性能

1.减低哈希冲突的次数——Hashtable通过令哈希表容量尽量为素数来降低

2.避免哈希表过度失衡——采用红黑树来平衡哈希表

3.哈希码的计算速度——HashMap采用移位操作来替代除法

小结:分析到这里我们对Hashtable和HashMap的整体实现和它们之间的区别有了一个大致的理解。当然这里面还涉及到很多知识,一篇博文肯定是难以讲清的,需要我们在JAVA的学习过程中再慢慢去探究。而在这次探究Hashtable和HashMap的过程中,有几点小感悟在这里记录一下。

1.要善于阅读源码——能写出这些API的都是大佬中的大佬,阅读源码就是一个和大佬交流的过程。

2.emmm,英语要学好——注释都是英语,有道翻译啥的都不是那么靠谱,能够自己看懂最好

3.看不到的源码可以通过样例测试来探究它的一些内部实现。

猜你喜欢

转载自blog.csdn.net/Alexwym/article/details/81177951