HashMap简单分析

HashMap简单分析

基于哈希表的Map接口实现。此实现提供所有可选的映射操作,并允许使用null值和null键。(除了非同步和允许使用null之外,HashMap类和Hashtable大致相同。)此类不保证映射顺序,特别是不保证该顺序永恒不变。 ——该描述来自JDK_API_1.6

  • HashMap概述

    在了解HashMap之前必须先了解什么是Hash表;简单介绍几种常见的数据结构

    1. 数组:采用一段连续的单元来存储数据;常见的特点是查找快,插入慢
    2. 链表:采用单个节点连接来存储数据,一般单向链表:上一个节点存储的下一个节点索引;常见的特点是查找慢,插入快
    3. 哈希表:根据关键码值进行直接访问的数据结构,也就是说,它通过把关键码值映射到表中的某一个位置,再次访问的时候通过关键值进行查询,插入和删除;但是当我们存储相同Hash值的元素时,我们这个时候就要面临解决Hash冲突,这里就不详细叙述Hash冲突的解决方式,可以自行百度或Google

    HashMap在Java中是使用哈希表来存储键值对(Entry),然而在是通过Key来决定Entry在Hash表中的位置,当Key出现了Hash冲突时,Java中采用的是“链表法”来解决冲突,如下图所示

    Markdown

  • HashMap源码分析

    HashMap.put()方法源码分析

    public V put(K key, V value) {
        // 判断是key是否为null,如果是null值的话,那么就使用null键值插入方式
        if (key == null)
            return putForNullKey(value);
        // 代码执行到此处,key不为null,获取key对象的hash值,然后再次hash
        int hash = hash(key.hashCode());
        // 获取key的hash值在Hash表中的位置
        int i = indexFor(hash, table.length);
        // 这个循环的作用是将Entry根据Hash值放入指定的Hash表中的并解决Hash冲突
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            // 获取内部类Entry中具体的一个Entry的当前的Key
            Object k;
            // 比较这个具体Entry中的key的Hash值和当前添加key的Hash值,以及两个key值是否相等
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                // 如果相等,那么将当前value赋值给oldValue,将添加进来的value赋值给当前key对应的value,最后将被覆盖的oldValue返回出去
                V oldValue = e.value;
                e.value = value;
                // 这个方法是一个空实现,我也不知道它有啥作用,JDK源码中方法注释大致翻译一下:这个方法这样情况下被执行,这个情况是当调用put()方法时,且key在当前Entry中是存在的,然后覆盖当前Entry中的value值
                e.recordAccess(this);
                return oldValue;
            }
        }
        // modCount作用是计算HashMap当前多少元素,之所以每次在添加元素都计数,
        // 是因为HashMap在元素超过当前容量时,需要对当前HashMap进行reHash,对
        // 当前HashMap进行扩容,然后对每一个元素进行重新填充,就是因为这个原因
        //,API中提示不保证该顺序是永久不变的
        modCount++;
        // 执行到这里的时候,说明该HashMap没有相同key值的Entry,那么就直接添加一个Entry
        addEntry(hash, key, value, i);
        // 最后因为没有替换oldValue,然后返回的是一个null的value
        return null;
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 首先拿出当前hash链表上的Entry,然后将原本的Entry挂在新创建的Entry下
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        // 当size大于HashMap的限制的时候,那么就开始扩容
        if (size++ >= threshold)
            resize(2 * table.length);
    }

    HashMap.get()方法分析

    public V get(Object key) {
        // 判断当前key值是否为null,假如为null的时候那么就使用特殊获取value的方法
        if (key == null)
            // 特殊或缺value的方法
            return getForNullKey();
        // 假如key不为null,那么根据传递key值,算出相应的hash值,找到指定的Entry位置
        int hash = hash(key.hashCode());
        // 获取Hash值对应的Entry,取到Entry链表下的所有Entry,遍历,直到找到响应的key值,找到后获取相应Entry的value
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        // 最后没有直到相应的Entry,那么就返回null
        return null;
    }
  • HashMap构造方法简单分析

    1. HashMap():创建一个初始容量为16,负载因子为0.75的HashMap
    2. HashMap(int initialCapacity):创建一个指定初始化容量initialCapacity大小,负载因子为0.75的HashMap
    3. HashMap(int initialCapacity, float loadFactor):创建一个指定初始化容量initialCapacity大小,负载因子为loadFactor的HashMap

    这里我们简单聊一下初始化容量和负载因子的作用,初始化容量其实可以从字面理解就是HashMap的最大长度,负载因子指的是HashMap的最大容量的实际使用率;作为负载因子来说,足够大的话,空间利用虽然充分,有可能出现查找效率低,之所以查找效率低,是因为一旦利用率大的话,哈希冲突可能越多,链表越长;负载因子越小,那么导致Hash列表过去稀疏,那么导致空间浪费

    上述博客参考:HashMap实现原理及源码分析深入Java集合学习系列:HashMap的实现原理java_api_1.6

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值