首页 > 编程语言 >HashMap&HashSet源码解读

HashMap&HashSet源码解读

时间:2024-08-22 19:38:07浏览次数:10  
标签:hash HashMap HashSet hashcode 链表 源码 哈希 红黑树

HashMap

HashSet需要提起的只有一句话

HashSet使用适配器模式包装了HashMap,所有的Value都是同一个Object对象,只有Key不一样,HashSet就是HashMap的KeySet

HashMap概述

一个允许Key为空也允许Value为空的哈希表

Hash冲突

当多个对象的hashcode计算结果一致时,需要处理冲突

  • 开放寻址法

    • 基本思想是当发生冲突时,通过探测来查找另外一个空的位置

    • 探测方法有线性探测、二次探测、双重hash

    • 线程探测就是:i+1、i+2、i+3.....

    • 二次探测就是间隔按照平方相加:i+12、i+22、i+3^2.....

    • 双重hash就是使用第二个hash函数探测间隔i + j * h2(key)

    • 使用开放寻址法时,删除操作需要标记为已删除,查找操作时需要根据探测策略生成探测序列,例如i+1、i+2、i+3。并且在探测时需要判断当前元素的hash值,防止探测位置溢出。

  • 冲突链表法

    • HashMap在Java8之前采用的就是冲突链表法,将所有相同哈希值的元素放在同一个链表(bucket)中

    • table太大和太小会有什么影响?

      • table太小会导致频繁扩容,并且会有大量元素在链表中,影响查询速度。

      • table太大会导致迭代效率低,因为需要遍历全部bucket。

负载因子

负载因子 = 当前元素数量 / 哈希表的容量

负载因子低:

  • 空间利用率低

  • 查询效率高

负载因子高:

  • 空间利用率高

  • 哈希碰撞多,导致查询效率低

因此如何平衡空间利用率和查询效率是如何设计HashMap需要做的事情。

HashMap的内部参数

  • DEFAULT_INITIAL_CAPACITY:初始容量16

  • MAXIMUM_CAPACITY:最大容量2^30

  • DEFAULT_LOAD_FACTOR:负载因子阈值0.75,超过自动扩容

  • TREEIFY_THRESHOLD:树化阈值8,超过链表变成红黑树

  • UNTREEIFY_THRESHOLD:还原阈值6,小于红黑树变成链表

  • MIN_TREEIFY_CAPACITY:最小树型化阈值64,容量大于该值时才能使用红黑树,否则优先扩容,而不是树化。

  • modCount:结构性修改标志,用于快速失败。

链表转为红黑树的逻辑

// 在 HashMap 中,当链表长度达到 TREEIFY_THRESHOLD 时,考虑链表转化为红黑树
if (binCount >= TREEIFY_THRESHOLD) {
    if (tab.length < MIN_TREEIFY_CAPACITY) {
        // 扩容前的处理
        resize();
    }
    treeifyBin(tab, hash);
}

解释:如果链表长度超过树化阈值8,此时考虑转为红黑树,但是不是马上转,而是如果当前哈希表容量小于64,则先扩容。扩容之后如果链表仍然很长,仍然大于8,则将链表转换为红黑树。

为什么优先扩容而不是优先转化红黑树

  • 减少负载因子,降低链表转化红黑树的频率,因为转化是一个复杂的操作,需要额外的开销和复杂性。

  • 降低链表长度,有效利用hash表空间,优化性能。

  • 一句话:用更多的空间换更多的性能。

为什么链表转树阈值为8,树转链表阈值为6?

  • 为了避免红黑树和链表频繁转换降低性能,设置了一段缓冲区。

扩容逻辑

1、触发扩容:当元素数量超过了当前容量 * 负载因子时,进行扩容,默认负载因子为0.75,可以在初始化时指定。

2、计算新容量:扩容为原来的两倍

3、创建新的数组:分配一个新的数组Node[],用于存储元素。

4、重新哈希Rehash

  • 迁移元素,重新遍历旧数组中的每个桶,重新计算每个元素的索引,移动到新数组中的相应位置。

  • 在Rehash过程中,如果某个bucket链表长度超过8,就转红黑树,此时不管MIN_TREEIFY_CAPACI

伪代码如下:

void resize() {
    // 计算新容量
    int newCapacity = oldCapacity * 2;
    // 创建新数组
    Node<K,V>[] newTable = (Node<K,V>[]) new Node[newCapacity];
    // 迁移旧数组中的所有元素到新数组
    transfer(newTable);
    // 更新哈希表的容量和阈值
    threshold = (int) (newCapacity * loadFactor);
    table = newTable;
}

void transfer(Node<K,V>[] newTable) {
    // 遍历旧数组中的每个桶
    for (Node<K,V> e : table) {
        if (e != null) {
            // 将桶中的所有元素重新哈希到新数组中
            Node<K,V> next;
            do {
                next = e.next;
                int i = indexFor(e.hash, newTable.length);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            } while (e != null);
        }
    }
}

判断相同元素的逻辑

首先计算hashcode,hashcode不相同的元素一定不相同

如果hashcode相同,再使用equals方法判断

这也是为什么hashcode和equals方法要一起重写的原因,即:

保证equals方法相同的时候hashcode也相同

hashcode的计算方法

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  • 如果为空直接把索引设置为0,避免异常

  • 计算元素自身的hashcode,但是为了避免自身hash方法的问题,加了一个哈希扰动

  • 扰动函数:将原来的hash值和无符号右移16位的值异或,增加hash函数的随机性,减少哈希冲突

  • 右移操作将哈希码的高 16 位移到低 16 位,有助于将哈希码的不同部分组合在一起。这样可以确保哈希值的变化更为复杂,不容易被预测。

标签:hash,HashMap,HashSet,hashcode,链表,源码,哈希,红黑树
From: https://www.cnblogs.com/lilizzyy/p/18374578

相关文章

  • 推荐一款强大的Web前端项目工程框架,实战检验很强大,高效稳定(附源码)
     前言在当前的Web前端开发领域,开发者经常面临如何快速构建高效、稳定、可维护的大型中台系统的问题。现有的解-决方案往往存在study曲线陡峭、组件库不够丰富、开发效率低下等痛点。为了解决这些问题,MyUI应运而生,提供了一个丰富、高效的Web前端项目工程框架。介绍MyUI是......
  • Kubernetes: client-go 源码剖析(一)
    kubernetes:client-go 系列文章:Kubernetes:client-go源码剖析(一)Kubernetes:client-go源码剖析(二)0.前言在看 kube-scheduler 组件的过程中遇到了 kube-scheduler 对于 client-go 的调用,泛泛的理解调用过程总有种隔靴搔痒的感觉,于是调转头先把 client-go 理清楚......
  • 【源码+论文】基于springboot的信息技术知识竞赛系统的设计与实现
    系统包含:源码+论文所用技术:SpringBoot+Vue+SSM+Mybatis+Mysql免费提供给大家参考或者学习,获取资料请私聊我目录第1章绪论 11.1选题动因 11.2目的和意义 11.3论文结构安排 2第2章开发环境与技术 32.1MYSQL数据库 32.2Tomcat介绍 32.3vue技术 42.4Sp......
  • 【论文+源码】基于springboot搭建的疫情管理系统
    系统包含:源码+论文所用技术:SpringBoot+Vue+SSM+Mybatis+Mysql免费提供给大家参考或者学习,获取资料请私聊我目录目录 III1绪论 11.1研究背景 11.2目的和意义 11.3论文结构安排 22相关技术 32.1springboot框架介绍 32.2B/S结构介绍 32.3Mysql数据......
  • 基于SpringBoot+Vue的学生作业管理系统的详细设计和实现(25年最新,附源码+论文+部署讲
    文章目录1.前言2.系统演示录像3.论文参考4.代码运行展示图5.技术框架5.1SpringBoot技术介绍5.2Vue技术介绍6.可行性分析7.系统测试7.1系统测试的目的7.2系统功能测试8.数据库表设计9.代码参考10.数据库脚本11.找我做程序,有什么保障?12.联系我们1.前......
  • PriorityQueue源码解析
    PriorityQueue优先级队列:默认每次取出权值最小的元素,元素的大小评判可以通过元素自身的自然顺序,也可以在构造时传入比较器进行定义顺序规则。用法//不传比较器PriorityQueue<Integer>pq=newPriorityQueue<>();pq.add(3);pq.add(4);pq.add(1);pq.add(2);//输出顺序1......
  • ArrayDeque源码解读
    ArrayDequeArrayDeque和LinkedList是Deque的两个通用实现,在使用Queue时,由于Queue只是一个接口,因此创建Queue时也会使用ArrayDeque为了实现在数组两端进行操作元素的需求,因此ArrayDeque使用循环数组作为底层数据结构,同时,ArrayDeque中定义了head和tail两个指针指向头和尾因为是循......
  • java+vue计算机毕设旅游景点预约系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着旅游业的蓬勃发展,人们对旅游体验的需求日益个性化与高效化。传统的旅游预订方式往往存在信息不对称、购票流程繁琐、景点拥堵等问题,影响了游客的......
  • java+vue计算机毕设开放实验室网上预约系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高等教育体系的不断发展和教育资源的日益丰富,实验室作为培养学生实践能力和创新精神的重要场所,其使用效率与管理水平成为衡量高校教学质量的重要......
  • java+vue计算机毕设农资电子监管系统的设计与实现【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着农业现代化的不断推进,农资产品的流通与管理成为保障农业生产高效、安全的重要环节。传统农资管理模式存在信息不对称、监管难度大、效率低下等问......