HashMap 和 HashTable

发布于 2024-04-14 19:45:48 字数 5419 浏览 32 评论 0

HashMap 和 Hashtable 两个类都实现了 Map 接口,二者保存 K-V 对(key-value 对);HashSet 则实现了 Set 接口,性质类似于集合。Hashtable 的应用非常广泛,HashMap 是新框架中用来代替 Hashtable 的类,也就是说建议使用 HashMap,不要使用 Hashtable。可能你觉得 Hashtable 很好用,为什么不用呢?这里简单分析他们的区别。

一、继承的父类不同

Hashtable 继承自 Dictionary 类,而 HashMap 继承自 AbstractMap 类,HashMap 是 Java1.2 引进的 Map interface 的一个实现。但二者都实现了 Map 接口。

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {

public class HashMap<K,V> extends AbstractMap<K,V>
	implements Map<K,V>, Cloneable, Serializable {

二、线程安全性不同

Hashtable 中的方法是 Synchronize 的,而 HashMap 中的方法在缺省情况下是非 Synchronize 的。在多线程并发的环境下,可以直接使用 Hashtable,不需要自己为它的方法实现同步,但使用 HashMap 时就必须要自己增加同步处理。

HashTable

public synchronized boolean isEmpty() {
    return count == 0;
}

HashMap

public boolean isEmpty() {
    return size == 0;
}

三、是否提供 contains 方法

HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsValue 和 containsKey,因为 contains 方法容易让人引起误解。
Hashtable 则保留了 contains,containsValue 和 containsKey 三个方法,其中 contains 和 containsValue 功能相同,实际上 containsValue 调用的是 contains 方法。

public synchronized boolean contains(Object value) {
    if (value == null) {
        throw new NullPointerException();
    }

    Entry<?,?> tab[] = table;
    for (int i = tab.length ; i-- > 0 ;) {
        for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
            if (e.value.equals(value)) {
                return true;
            }
        }
    }
    return false;
}

public boolean containsValue(Object value) {
    return contains(value);
}

四、key 和 value 是否允许 null 值

其中 key 和 value 都是对象,并且不能包含重复 key,但可以包含重复的 value。

Hashtable 中,key 和 value 都不允许出现 null 值。HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null。当 get() 方法返回 null 值时,可能是 HashMap 中没有该键,也可能使该键所对应的值为 null。因此,在 HashMap 中不能由 get() 方法来判断 HashMap 中是否存在某个键, 而应该用 containsKey() 方法来判断。

HashTable

  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();
    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)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

HashMap

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

五、两个遍历方式的内部实现上不同

Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了 Enumeration 的方式 。

六、hash 值不同

哈希值的使用不同,HashTable 直接使用对象的 hashCode。而 HashMap 重新计算 hash 值。

七、内部实现使用的数组初始化和扩容方式不同

Hashtable 和 HashMap 它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable 中 hash 数组默认大小是 11,增加的方式是 old*2+1。HashMap 中 hash 数组的默认大小是 16,而且一定是 2 的指数。

Java 5 提供了 ConcurrentHashMap,它是 HashTable 的替代,比 HashTable 的扩展性更好。

我们能否让 HashMap 同步?

HashMap 可以通过下面的语句进行同步:

Map m = Collections.synchronizeMap(hashMap);

关于 ConcurrentHashMap

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {

Hashtable 和 HashMap 有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用 Hashtable,而如果你使用 Java 5 或以上的话,请使用 ConcurrentHashMap 吧。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

有深☉意

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

内心激荡

文章 0 评论 0

JSmiles

文章 0 评论 0

左秋

文章 0 评论 0

迪街小绵羊

文章 0 评论 0

瞳孔里扚悲伤

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文