java中HashMap的实现原理

HashMap底层实现原理

JDK1.7及以前的版本中 ,HashMap 底层由 数组+ 链表 实现。

新建一个 HashMap的时候就会初始化一个数组。数组中的元素我们称为 Entry,每个 Entry其实就是一个 键值对,并且包含一个指向下一个元素的引用,这就构成了链表。当需要存储一个 键值对(Entry对象) 时,会根据hash算法来决定其在数组中的位置。当需要取出一个 Entry 对象时,也会根据 hash算法找到其在数组中的存储位置,再根据 equals 方法从该位置的链表中取出 Entry。

HashMap 结合了 ArrayList 的查询效率高的特点以及 LinkedList 插入效率高的特点,但是如果我们要存储的数据过于庞大,肯定会造成多次 哈希冲突,这样一来,链表上的节点就会堆积很多,在做查询的时候效率又会变得很低,失去了 HashMap本来的特点

 在JDK1.8 及之后的版本中,HashMap 底层由 数组 + 链表 + 红黑树 实现。当节点数不大于 8 时,还是一个链表结构,只不过插入节点时变成了尾插法,当节点数大于 8 之后,将链表结构转换为红黑树结构,复杂度也从 O(n) 变成了 O(logn).

HashMap.put

put方法在hashmap中起插入的作用,即在当前的hashmap上添加新元素。

主要流程:

  1. 判断table数组是否为空,或者为 null,否则执行 resize() 扩容操作
  2. 使用 key.hashcode() 将 key 转换为 数组下标 i , 如果 table[i] =null,直接新建节点添加即可,转入 6 ;如果 table[i] != null,则转向 3;
  3. 判断 table[i] 的首个元素是否和 key 一样,如果相同(equals判断)则直接覆盖 value,否则转向 4
  4. 判断 table[i] 是否为 红黑二叉树,如果是,则直接插入键值对,否则转向 5
  5. 遍历 table[i],判断链表长度是否大于 8 ,若是 则把链表转换为 红黑树,进行插入操作;否则进行链表插入操作;如果链表中已存在 key 则直接覆盖 value
  6. 插入成功后,判断实际存在的键值对数量是否超过了 threshold ,如果操作则需要扩容。
public v put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    // 通过 key 计算hash值
    int hash = hash(key);
    // 通过 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))) {
            // 如果两个 hash 值相同,或者键相同,则修改 value
            V oldValue = e.value;
            e.value = Value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    // 可能两个 hash 值不同,但计算出的 index 一样,这就发生了 hash碰撞
    // 使用头插法,将其放在链表头部
    addEntry (hash, key, value, i)
    return null;
}

HashMap.get

get方法在hashmap中起取值的作用,即根据 key 获取对应的 value值

主要流程:

  1. 首先判断数组是否为空,如果为空直接返回null;
  2. 使用 hashcode() 计算 key的索引 i ,如果 table[i] 为空,返回null;否则判断key值是否相同,若相同则返回该key的value
  3. 如果 key 值不相同,则往后遍历;
  4. 如果 table[i] 是红黑树,则调用红黑树的 getTreeNode() 进行查找
  5. 如果 table[i] 是链表,则遍历链表查找
public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //若哈希表不为空,并且当前索引位置有元素
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //若在一个桶中,并且当前索引位置的key值与要查找的key值相等
            //说明找到了,返回该值
            if (first.hash == hash && 
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
                //若key值不相同,且还有元素
            if ((e = first.next) != null) {
                //如果此时已经树化,则调用红黑树的getTreeNode()查找
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    //若没有树化,则在从前往后遍历链表,直到找到该元素
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        //若找不到则返回null
        return null;
    }

JavaHashMap是一种散列表,它存储键值对,并且允许快速的插入和查找。HashMap实现原理如下: 1. HashMap内部维护了一个数组,数组的每个元素都是一个链表的头结点,每个链表存储了哈希值相同的键值对。 2. 当向HashMap插入一个键值对时,首先计算该键的哈希值,然后根据哈希值找到对应的数组元素,如果该元素为空,则直接将键值对插入到该位置;如果该元素不为空,则遍历该元素对应的链表,查找是否已经存在相同的键,如果存在,则更新该键对应的值,否则将该键值对插入到链表的末尾。 3. 当从HashMap查找一个键值对时,首先计算该键的哈希值,然后根据哈希值找到对应的数组元素,遍历该元素对应的链表,查找是否存在相同的键,如果存在,则返回该键对应的值,否则返回null。 以下是两种遍历HashMap的方法: 1. 使用keySet()方法遍历HashMap: ```java Map<String, Integer> map = new HashMap<>(); Iterator<String> iter = map.keySet().iterator(); while (iter.hasNext()) { String key = iter.next(); Integer val = map.get(key); // do something with key and val } ``` 2. 使用entrySet()方法遍历HashMap: ```java Map<String, Integer> map = new HashMap<>(); Iterator<Map.Entry<String, Integer>> iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, Integer> entry = iter.next(); String key = entry.getKey(); Integer val = entry.getValue(); // do something with key and val } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值