HashMap和HashTable区别

1,线程安全

Hashtable是线程安全的,HashMap不是线程安全的。

为什么说HashTable是线程安全的?

来看下Hashtable的源码,Hashtable所有的元素操作都是synchronized修饰的,而HashMap并没有。

public synchronized V put(K key, V value);
public synchronized V get(Object key);
...

2,性能优劣

既然Hashtable是线程安全的,每个方法都要阻塞其他线程,所以Hashtable性能较差,HashMap性能较好,使用更广。

如果要线程安全又要保证性能,建议使用JUC包下的ConcurrentHashMap。

3,NULL

Hashtable是不允许键或值为null的,HashMap的键值则都可以为null。

那么问题来了,为什么Hashtable是不允许KEY和VALUE为null,而HashMap则可以?

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();

        ...

}     

HashMap hash方法逻辑:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

可以看出Hashtable key为null会直接抛出空指针异常,值为null手动抛出空指针异常,而HashMap的逻辑对null作了特殊处理。

4,实现方式

Hashtable的继承源码:

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

HashMap的继承源码:

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

可以看出两者继承的类不一样,Hashtable继承了Dictionary类,而HashMap继承的是AbstractMap类。

5,容量扩容

HashMap的初始容量为:16,Hashtable初始容量为:11,两者的负载因子默认都是:0.75。

/**
 * Constructs a new, empty hashtable with a default initial capacity (11)
 * and load factor (0.75).
 */
public Hashtable() {
    this(11, 0.75f);
}

/**
 * Constructs an empty <tt>HashMap</tt> with the default initial capacity
 * (16) and the default load factor (0.75).
 */
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

当前容量大于总容量*负载因子时,HashMap扩容规则为当前容量翻倍,Hashtable扩容规则为当前容量翻倍+ 1。

6,迭代器

HashMap中的Iterator迭代器是fail-fast的,而Hashtable的Enumerator不是fail-fast的。

所以,当其他线程改变了HashMap的结构,如:增加,删除元素,将会抛出ConcurrentModificationException异常,而Hashtable则不会。

可以来看下这个区别的演示:

public static void main(String[] args) {
    Map<String, String> hashtable = new Hashtable<>();
    hashtable.put("t1", "1");
    hashtable.put("t2", "2");
    hashtable.put("t3", "3");

    Enumeration<Map.Entry<String, String>> iterator1 = (Enumeration<Map.Entry<String, String>>) hashtable.entrySet().iterator();
    hashtable.remove(iterator1.nextElement().getKey());
    while (iterator1.hasMoreElements()) {
        System.out.println(iterator1.nextElement());
    }

    Map<String, String> hashMap = new HashMap<>();
    hashMap.put("h1", "1");
    hashMap.put("h2", "2");
    hashMap.put("h3", "3");

    Iterator<Map.Entry<String, String>> iterator2 = hashMap.entrySet().iterator();
    hashMap.remove(iterator2.next().getKey());
    while (iterator2.hasNext()) {
        System.out.println(iterator2.next());
    }

}

输出信息:

t2=2
t1=1
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1476)
    at java.util.HashMap$EntryIterator.next(HashMap.java:1474)
    at cn.javastack.Test.main(Test.java:37)

看到了吧?

所以,这条同样也是Enumeration和Iterator的区别。


原文地址:https://www.cnblogs.com/fengyuduke/p/10608401.html