美文网首页
HashMap和HashTable区别

HashMap和HashTable区别

作者: Itsmely队长 | 来源:发表于2018-12-03 14:20 被阅读0次

一、HashMap中的方法都属于异步操作(非线程安全),允许保存有null数据;

HashMap内部包含了一个Entry类型的数据table:

transient Entry[] table;

Entry存储着键值对,它包含了四个字段,hashcode、k、v、next。从next字段我们可以看出Entry是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。


image.png

拉链法的工作原理:

HashMap<String, String> map = new HashMap<>();
map.put("K1", "V1");
map.put("K2", "V2");
map.put("K3", "V3");

1、新建一个 HashMap,默认大小为 16;
2、插入 <K1,V1> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到 所在的桶下标 115%16=3。
3、插入 <K2,V2> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。
4、插入 <K3,V3> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,插在 <K2,V2> 前面。

应该注意到链表的插入是以头插法方式进行的,例如上面的 <K3,V3> 不是插在 <K2,V2> 后面,而是插入在链表头部。

查找需要分成两步进行:

1、计算键值对所在的桶;
2、在链表上顺序查找,时间复杂度显然和链表的长度成正比。

put 操作

public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    // 键为 null 单独处理
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    // 确定桶下标
    int i = indexFor(hash, table.length);
    // 先找出是否已经存在键为 key 的键值对,如果存在的话就更新这个键值对的值为 value
    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;
        }
    }

    modCount++;
    // 插入新键值对
    addEntry(hash, key, value, i);
    return null;
}

HashMap 允许插入键为 null 的键值对。但是因为无法调用 null 的 hashCode() 方法,也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使用第 0 个桶存放键为 null 的键值对。

从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。

二、HashTable中的方法都属于同步操作(线程安全),不允许保存有null数据。

相关文章

网友评论

      本文标题:HashMap和HashTable区别

      本文链接:https://www.haomeiwen.com/subject/gasycqtx.html