Hashtable与HashMap的区别

来源:互联网 发布:cs1.5优化教程视频 编辑:程序博客网 时间:2024/04/30 15:55

先说说Hashtable,我们看到java api文档中介绍,其实现了java.util.Dictonary< K ,V >类

如文档中介绍,Any non-null object can be used as a key or as a value,即不能将空对象放入Hashtable中

Unlike the new collection implementations, Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.

在看上面一句话,意思是:不像集合框架的实现类,Hashtable是同步的(我也不太会翻译这句话,我自己理解的意思是Hashtable是线程安全的,即对于某些会出现线程安全问题的方法,都用synchronized来修饰)。如果不需要线程安全的实现,则建议使用HashMap来替代Hashtable。如果需要高并发线程安全的实现,则推荐使用ConcurrentHashMap来替代Hashtable

Hashtable是过去遗留下来的类,目的只是为了支持老的操作

下面我们来看看Hashtable的部分源码

//注意这里1!!!! public synchronized V put(K key, V value) {        // Make sure the value is not null        //注意这里2!!!!        if (value == null) {            throw new NullPointerException();        }        // Makes sure the key is not already in the hashtable.        Entry tab[] = table;        int hash = hash(key);        int index = (hash & 0x7FFFFFFF) % tab.length;        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {            if ((e.hash == hash) && e.key.equals(key)) {                V old = e.value;                e.value = value;                return old;            }        }        modCount++;        if (count >= threshold) {            // Rehash the table if the threshold is exceeded            rehash();            tab = table;            hash = hash(key);            index = (hash & 0x7FFFFFFF) % tab.length;        }        // Creates the new entry.        Entry<K,V> e = tab[index];        tab[index] = new Entry<>(hash, key, value, e);        count++;        return null;    }

下面我们来解读一下这个put方法

首先,注意1:是这个方法用了synchronized方法修饰,说明其是线程安全的

注意2:确定其存入的值不能为空,否则抛出空指针异常

首先我们要明白,设计者用数组保存键的信息(此处是键的信息,而不是键的本身),数组并不保存键的本身,而是通过键对象生成一个数字,将其作为数组的下标,即hashcode。

所以我们并不能保证不同的对象会生成不同的hashcode,有可能他们会生成相同的hashcode,这时候会出现”hash collision”。冲突由外部链接进行处理:数组并不直接保存值,而是保存值的list。然后对list中的值使用equals方法进行现行查询。这部分的查询自然比较慢,但是散列函数取得好的话,数组的每个位置就只有较少的值。

1.确认存入的键在hashtable中还未存在

2.首先根据其的hash码算出数组的下标,得到在数组中所对应的Entry< K , V >键值对,再跟键对象的hash码判断容器中是否存在这个键(若hash码相同,再依据equals进行查询),即满足(e.hash == hash) && e.key.equals(key)此条件时,说明容器中存在此键值对,则返回其值,否则继续循环

3.若不满足上述条件,则说明容器中无此键值对,但是根据hash码得出其键值对非空,则重新计算hash码,则将键值对装入hashtable的Entry链表中,并且将其放入table数组中,方便第二步的查询和get()方法的使用

相应的其一些可能涉及线程安全的方法如remove(Object key),contains(Object value) 等都用synchronized来修饰,以确保hashtable是线程安全的

下面我们来看看HashMap

HashMap继承的是java.util.AbstractMap< K , V >类,根据java API文档的介绍,HashMap permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.)

上面就说出了HashMap和Hashtable最重要的区别,即Hashtable除了不允许非空值和线程安全,其和HashMap没什么区别

**
即它们的区别:
1.HashMap允许有空的键值对,即空值和空键,而Hashtable不允许,若存入空值则抛出异常
2.Hashtable是线程安全的,HashMap是非线程安全的
**

现在看HashMap的put方法的源码

//注意这里1!!!!public V put(K key, V value) {        if (table == EMPTY_TABLE) {            inflateTable(threshold);        }        //注意这里2!!!!        if (key == null)            return putForNullKey(value);        int hash = hash(key);        int i = indexFor(hash, table.length);        for (Entry<K,V> e = table[i]; e != null; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }

除了上面的两大区别以外,他们的执行过程都基本一样

我自己看源码觉得还有一点小小的不同的是:当发生了hash码冲突(table中的下标冲突)的时候,要将刚放入的键值对放到table数组中,Hashtable的做法是,重新计算hash值,调用rehash()方法,而HashMap的做法是先将table的数组大小进行扩容,再重新计算其hash值,再将键值对存入对应的table下标下!!!

0 0