HashMap---数据结构

目录

一、基本数据结构

二、树化与退化

三、索引计算

四、put方法和扩容

五、并发问题

六、key的设计


一、基本数据结构

        在jdk1.7版本的时候,hashmap结构主要是使用数组 + 链表的格式,而在jdk1.8版本中,hashmap的数据结构增加了一种“红黑树”的结构,即数组 + (链表 | 红黑树)的一种格式。

二、树化与退化

 树化意义

        红黑树用来避免 DoS 攻击,防止链表超长时性能下降,树化应当是偶然情况,是保底策略。

        hash 表的查找,更新的时间复杂度是 $O(1)$,而红黑树的查找,更新的时间复杂度是 $O(log_2⁡n )$,TreeNode 占用空间也比普通 Node 的大,如非必要,尽量还是使用链表。

        hash 值如果足够随机,则在 hash 表内按泊松分布,在负载因子 0.75 的情况下,长度超过 8 的链表出现概率是 0.00000006,树化阈值选择 8 就是为了让树化几率足够小

树化规则

        当链表长度超过树化阈值 8 时,先尝试扩容来减少链表长度,如果数组容量已经 >=64,才会进行树化

退化规则

        情况1:在扩容时如果拆分树时,树元素个数 <= 6 则会退化链表

        情况2:remove 树节点时,若 root、root.left、root.right、root.left.left 有一个为 null ,也会退化为链表

三、索引计算

索引计算方法

  • 首先,计算对象的 hashCode()

  • 再进行调用 HashMap 的 hash() 方法进行二次哈希

    • 二次 hash() 是为了综合高位数据,让哈希分布更为均匀

  • 最后 & (capacity – 1) 得到索引

数组容量为何是 2 的 n 次幂

  1. 计算索引时效率更高:如果是 2 的 n 次幂可以使用位与运算代替取模。

  2. 扩容时重新计算索引效率更高: hash & oldCap == 0 的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap。

注意

  • 二次 hash 是为了配合 容量是 2 的 n 次幂 这一设计前提,如果 hash 表的容量不是 2 的 n 次幂,则不必二次 hash。

  • 容量是 2 的 n 次幂 这一设计计算索引效率更好,但 hash 的分散性就不好,需要二次 hash 来作为补偿,没有采用这一设计的典型例子是 Hashtable。

四、put方法和扩容

put 流程

  1. HashMap 是懒惰创建数组的,首次使用才创建数组。

  2. 计算索引(桶下标)。

  3. 如果桶下标还没人占用,创建 Node 占位返回。

  4. 如果桶下标已经有人占用:

    1. 已经是 TreeNode 走红黑树的添加或更新逻辑。

    2. 是普通 Node,走链表的添加或更新逻辑,如果链表长度超过树化阈值,走树化逻辑。

  5. 返回前检查容量是否超过阈值,一旦超过进行扩容。

1.7 与 1.8 的区别

  1. 链表插入节点时,jdk1.7 是头插法,jdk1.8 是尾插法。

  2. jdk1.7 是大于等于阈值且没有空位时才扩容,而 jdk1.8 是大于阈值就扩容。

  3. jdk1.8 在扩容计算 Node 索引时,会优化。

扩容(加载)因子为何默认是 0.75f

  1. 在空间占用与查询时间之间取得较好的权衡

  2. 大于这个值,空间节省了,但链表就会比较长影响性能

  3. 小于这个值,冲突减少了,但扩容就会更频繁,空间占用也更多

五、并发问题

扩容死链(1.7 会存在)

  • e 和 next 都是局部变量,用来指向当前节点和下一个节点

  • 线程1(绿色)的临时变量 e 和 next 刚引用了这俩节点,还未来得及移动节点,发生了线程切换,由线程2(蓝色)完成扩容和迁移

  • 线程2 扩容完成,由于头插法,链表顺序颠倒。但线程1 的临时变量 e 和 next 还引用了这俩节点,还要再来一遍迁移

  • 第一次循环

    • 循环接着线程切换前运行,注意此时 e 指向的是节点 a,next 指向的是节点 b

    • e 头插 a 节点,注意图中画了两份 a 节点,但事实上只有一个(为了不让箭头特别乱画了两份)

    • 当循环结束是 e 会指向 next 也就是 b 节点

  • 第二次循环

    • next 指向了节点 a

    • e 头插节点 b

    • 当循环结束时,e 指向 next 也就是节点 a

  • 第三次循环

    • next 指向了 null

    • e 头插节点 a,a 的 next 指向了 b(之前 a.next 一直是 null),b 的 next 指向 a,死链已成

    • 当循环结束时,e 指向 next 也就是 null,因此第四次循环时会正常退出

数据错乱(1.7,1.8 都会存在)

        也就是多个线程对一个同一个值的key进行存储的时候,会产生覆盖问题。

        也包括仅仅修改了当前线程中的该副本的值,但并没有及时更新,进而导致一种错误。

六、key的设计

key 的设计要求

  1. HashMap 的 key 可以为 null,但 Map 的其他实现则不然

  2. 作为 key 的对象,必须实现 hashCode 和 equals,并且 key 的内容不能修改(不可变)

  3. key 的 hashCode 应该有良好的散列性

如果 key 可变,例如修改了 age 会导致再次查询时查询不到。

public class HashMapMutableKey {
    public static void main(String[] args) {
        HashMap<Student, Object> map = new HashMap<>();
        Student stu = new Student("张三", 18);
        map.put(stu, new Object());

        System.out.println(map.get(stu));

        stu.age = 19;
        System.out.println(map.get(stu));
    }

    static class Student {
        String name;
        int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age && Objects.equals(name, student.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
}

String 对象的 hashCode() 设计

  • 目标是达到较为均匀的散列效果,每个字符串的 hashCode 足够独特

  • 字符串中的每个字符都可以表现为一个数字,称为 $S_i$,其中 i 的范围是 0 ~ n - 1

  • 散列公式为: $S_0∗31^{(n-1)}+ S_1∗31^{(n-2)}+ … S_i ∗ 31^{(n-1-i)}+ …S_{(n-1)}∗31^0$

  • 31 代入公式有较好的散列特性,并且 31 * h 可以被优化为

    • 即 $32 ∗h -h $

    • 即 $2^5 ∗h -h$

    • 即 $h≪5 -h$

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/487718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用git下载github/gitee仓库部分或单个文件的方法

前言 有些时候在github或者gitee仓库中我们只需要下载整个项目中的我门需要的那一部分文件夹或文件就行了&#xff0c;不需要下载所有的项目。这样可以节省很多流量和时间 步骤 1.建立一个新的 git 本地仓库 这里我在D:\test中初始化 命令&#xff1a; git init2.在本地仓…

指定日本访学|普通高校教师获九州大学邀请函CSC改派成功

R老师DIY申请到某国的访学邀请函并获批CSC。但在派出阶段&#xff0c;对方因故不能接收&#xff0c;亟需获得新的邀请函以申请改派&#xff0c;这次其指定日本。最终我们用日本九州大学的邀请函协助R老师CSC改派成功&#xff0c;并赴日开展访问学者研究。 R老师背景&#xff1a…

力扣刷题Days25-45. 跳跃游戏 II(js)

目录 1&#xff0c;题目 2&#xff0c;代码 贪心算法正向查找 3&#xff0c;学习 解题思路 具体代码处理 数组遍历的最后边界的处理&#xff1a; 1&#xff0c;题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向…

深度学习教程(一):cuda的安装教程转载

0.前言 啊&#xff0c;好久没更新了&#xff0c;跟大家汇报一下我最近都在干嘛。我的导师&#xff08;未来版&#xff0c;毕竟我才开始准备考研&#xff09;在年后让我以我以前做过的实验为基础尽量在本科期间就能搞成一篇小论文&#xff08;渐渐成长为sci&#xff09;,导师还催…

下载最新VMware,专业版本

VMware - Delivering a Digital Foundation For BusinessesRun any app on any cloud on any device with a digital foundation built on VMware solutions for modern apps, multi-cloud, digital workspace, security & networking.https://www.vmware.com/ 官网地址

CMakeLists生成动态库.so和静态库.a

一、下载NDK CMake - NDK : 26.2.11394342 或 23.1.7779620 - CMake : 3.22.1 二、新建android\app\CMakeLists.txt 文件CMakeLists.txt内容 cmake_minimum_required(VERSION 3.4.1) #mker为项目名称 project(mker)#设置生成的so动态库最后输出的路径 set(CMAKE_LIBRARY_OUTP…

Visual Basic6.0零基础教学(4)—编码基础,数据类型与变量

编码基础,数据类型与变量 文章目录 编码基础,数据类型与变量前言一、VB中的编程基础二、VB的基本字符集和词汇集1、字符集2、词汇集 VB中的数据类型VB中的变量与常量一.变量和常量的命名规则二.变量声明1.用Dim语句显式声明变量三. 常量 运算符和表达式一. 运算符 1. 算术运算符…

promethus的安装使用

1、# 软件下载地址 https://prometheus.io/download/ https://grafana.com/grafana/download https://prometheus.io/download/ Prometheus是一套开源的监控&报警&时间序列数据库的组合,起始是由SoundCloud公司开发的。 Prometheus 的优点 1、非常少的外部依赖,安装…

解决sqlalchemy执行语句提示Not an executable object

问题&#xff1a; from sqlalchemy import create_engine# 数据库的变量 HOST 127.0.0.1 PORT 3306 DATA_BASE itbz USERroot PWD123456 # DB_URL f数据库的名驱动名://{USER}:{PWD}{HOST}:{PORT}/{DATA_BASE} DB_URL fmysqlpymysql://{USER}:{PWD}{HOST}:{PORT}/{DATA_B…

定时器 c++ 基于时间线

获取当前时间std::chrono::system_clock::now(); std::chrono::time_point_cast<std::chrono::milliseconds>(now) 是 std::chrono 标准库中的一个函数调用&#xff0c;用于将时间点 now 转换为毫秒级别精度的时间点。 friend class timermanger; 表示将类 timermanger …

24年3月下半笔记(个人向)(更新中)

3.19 开始复工复产了 发现poe网站上可以免费用chatgpt&#xff0c;用了两天就彻底产生依赖性了 继续看d2l&#xff0c;之前看到第三章结束&#xff0c;今天从4.1看到4.6 第四章讲mlp&#xff0c;老生常谈&#xff0c;各种激活函数ReLU、sigmoid、tanh 然后是防止过拟合&am…

Verilog刷题笔记43

题目&#xff1a;Exams/m2014 q4b 解题&#xff1a; module top_module (input clk,input d, input ar, // asynchronous resetoutput q);always(posedge clk,posedge ar)beginif(ar1)q<0;elseq<d;endendmodule结果正确&#xff1a; 补充&#xff1a; 同步复位和异步…

Jenkins+Ant+Jmeter接口自动化集成测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、Jenkins安装配置 1、安装配置JDK1.6环境变量&#xff1b; …

抓取京东/淘宝类数据#Javascript#商品详情图片

提出问题 如何在京东商城爬取出各个商品的相关信息(价格、名称、评价、店铺名等等)&#xff0c;比如&#xff0c;打开web京东网站&#xff0c;那么商品展示列表的所有商品的信息&#xff0c;怎么爬下来&#xff0c;怎么保存到表格中&#xff1f; 我们来看看怎么实现这个功能。…

手写springboot启动器, 学习SpringBoot的最佳实践

自己手写的SpringBoot启动器, 是一个学习了解SpringBoot启动逻辑和了解springboot原理的不错的实践Demo. 废话不多说,直接上代码: 项目结构 maven多项目结构, myspringboot 自己手写的SpringBoot启动器 service-demo 用来测试SpringBoot启动器的示例项目 项目pom依赖 1.…

Istio 部署 Spring Coud 微服务应用

Istio 服务部署 这篇文章讲述如何将 Java Spring Cloud 微服务应用部署到 Istio mesh 中。 准备基础环境 使用 Kind 模拟 kubernetes 环境。文章参考&#xff1a;https://blog.csdn.net/qq_52397471/article/details/135715485 在 kubernetes cluster 中安装 Istio 创建一…

接口用例之好用例和坏用例

自动化测试的重要性显而易见&#xff0c;但自动化测试又无法解决所有问题&#xff0c;所以说完全依赖自动化是不可能的&#xff0c;但完全没有自动化是万万不能。在软件开发项目中&#xff0c;重度依赖人力进行持续回归是一件非常枯燥的重复工作。企业需要花费大量的时间和金钱…

代码随想录算法训练营第33天|1005.K次取反后最大化的数组和|134. 加油站|135. 分发糖果

代码随想录算法训练营第33天|1005.K次取反后最大化的数组和|134. 加油站|135. 分发糖果 1005.K次取反后最大化的数组和 本题简单一些&#xff0c;估计大家不用想着贪心 &#xff0c;用自己直觉也会有思路。 https://programmercarl.com/1005.K%E6%AC%A1%E5%8F%96%E5%8F%8D%E5%…

【QA】MySQL导出某数据库的所有数据为sql文件,包含建库命令、建表命令。

文章目录 前言Windows系统下 | mysqldump导出数据库数据Docker中导入初始化数据【补充】通过命令行&#xff0c;执行sql文件&#xff0c;将数据导入到数据库在MySQL外面执行在MySQL中执行 前言 我们在用docker部署mysql项目的时候&#xff0c;往往需要对数据库进行数据初始化。…

PLC_博图系列☞RS:复位/置位触发器

PLC_博图系列☞RS&#xff1a;复位/置位触发器 文章目录 PLC_博图系列☞RS&#xff1a;复位/置位触发器背景介绍RS&#xff1a;复位/置位触发器说明参数示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 RS 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别…