HashMap的总结

关于HashMap的一些回顾(JDK1.8)

HashMap的数据结构(数组+链表+红黑树),存储元素的方式(key-value),HashMap是通过前面的数据结构进行存储元素,也就是它的put方法,简单描述一下就是在存储元素时,会根据元素key的hashCode方法和当前数组长度-1进行运算得到元素存储在数组的指定下标位置,如果当前位置已经存在元素(哈希碰撞),那么就在当前位置拉出一个链表,当然还有判断key是否为null,元素是否存放重复等问题,简要的就这样。**
简单实现一下HashMap,摘自(bugstack.cn)代码片段

public static void main(String[] args) {
        String str = "a,b,c,d,e,f,g,h";
        List<String> dataList = Collections.singletonList(str);

        //定义存放元素的数组
        String[] tab = new String[10];

        for (String key : dataList) {
            //计算索引位置,也是发生哈希碰撞的代码片段,因为计算出来的下标位置可能是一样的
            int idx = key.hashCode() & (tab.length - 1);
            System.out.println(String.format("key 值=%s Idx=%d", key, idx));
            //判断当前元素是否已经有元素存在,如果不存在,就将当前元素进行存储
            if (null == tab[idx]) {
                tab[idx] = key;
                continue;
            }
            //如果当前数组位置有元素那么其他元素排在该元素后面拉出一个链表
            tab[idx] = tab[idx] + "->" + key;
        }
        System.out.println(tab.toString());
    }

这样HashMap就会面临一些问题,元素的存放都需要计算所在下标,如果一直发生碰撞,那么就失去了它的意义,还有碰撞的时候拉的链表越来越长怎么去优化,数组存放已经达到阈值,怎么扩容,怎么把原有的元素拆分出去等,这些问题都可以归纳为,扰动函数,初始化容量,负载因子,扩容方法和链表转换红黑树等。

1.扰动函数
HashMap在存放元素的时候,会根据key的hashCode进行扰动计算,把Hash值右移16位,也就正好是自己长度的一
半,之后与原哈希值做异或运算,这样就混合了原哈希值中的高位和低位,增大
了随机性,源码如图:
在这里插入图片描述
说白了,使用扰动函数就是为了增加随机性,让数据元素更加均衡的散
列,数据分配均匀,也就是散列的效果更好,减少了 hash 的碰撞,让数据存
放和获取的效率更佳。
2.初始化容量
HashMap的散列数组需要是2的倍数(2的倍数作为HashMap的容量值,HashMap在存放元素的时候会更加的散列,减少碰撞),如果在初始化的时候指定容量不是2的倍数HashMap会怎么办?比如:HashMap<Object, Object> map = new HashMap<>(17),初始化了一个长度为17的HashMap,他的处理方式如下

根据17计算阀的得过程:

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

用图展示
在这里插入图片描述

位移运算:例子,1 << 5解释一下

		int res = 1 << 5;
        /*
         * 1 * 2
         *   ⬇
         * 2 * 2
         *   ⬇
         * 4 * 2
         *   ⬇
         * 8 * 2
         *   ⬇
         * 16 * 2
         *  =32
         */

结果最终为32,那么就会从17改为32作为新的容量。
3.负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
负载因子是做什么的?
负载因子,可以理解成一辆车可承重重量超过某个阀值时,把货放到新的车上。
那么在 HashMap 中,负载因子决定了数据量多少了以后进行扩容。这里要提到上
面做的 HashMap 例子,我们准备了 7 个元素,但是最后还有 3 个位置空余,2 个
位置存放了 2 个元素。 所以可能即使你数据比数组容量大时也是不一定能正正
好好的把数组占满的,而是在某些小标位置出现了大量的碰撞,只能在同一个位
置用链表存放,那么这样就失去了 Map 数组的性能。
所以,要选择一个合理的大小下进行扩容,默认值 0.75 就是说当阀值容量占了
3/4 时赶紧扩容,减少 Hash 碰撞。
同时 0.75 是一个默认构造值,在创建 HashMap 也可以调整,比如你希望用更多
的空间换取时间,可以把负载因子调的更小一些,减少碰撞。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值