为啥突然想着看HashMap源码了?

  无意间看到有人说HashMap能考验Java程序员的基本功,之前我作为面试官帮公司招人的时候偶尔问起HashMap,大部分人回答基本都会用,且多数仅停留在put,get使用上面,实际上HashMap涉及的知识远远不止put和get那么简单。HashMap涉及线程、内存模型、Hash计算、链表结构、二进制运算等问题,可以说HashMap能考验一个Java程序员的技术功底。作为具备一定工作经验的技术人员,我们必须回头去恶补基础,凡是技术点都要努力去搞清楚是什么,为什么,怎么用。

   HashMap基本概念及原理:

   如果我们想要很快的查询一个数据,最好将其用数组存储,因为数组查询速度快,但是数组的长度不可以修改,所以它添加元素很麻烦,需要创建一个更大的数组,然后把老数组的元素按顺序拷贝到新数组中,而我们想要添加元素,最好使用链表去存储,因为链表是离散的,所以在添加或者删除的时候,只会修改局部的内容,也正是因为链表是离散的,它的位置在内存中不是一直固定的(指的是不连续),每次要查找下一个元素的时候,都需要读取其位置信息,所以链表的查询很慢。那有没有一种数据结构,它的查询很快,添加和删除速度也很快呢?答案是肯定的,结合数组和链表的优点,哈希表诞生了。

  HashMap基于哈希表的Map接口实现,是以key-value的存储形式存在,即主要用来存放键值对。HashMap的实现不是同步的,这意味着它不是线程安全的。数组是HashMap的主体,链表则是为了解决hash冲突而存在的,所谓hash冲突就是两个对象调用hashCode()方法计算的hash值相同导致计算的数组索引也相同。

  JDK1.8之后在解决Hash冲突时有了较大的变化,当链表长度大于边界值(默认为8)且当前数组长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。另外需要注意的是,当链表长度大于8但是数组长度小于64,此时也并不会将链表变成红黑树,而是选择扩容。这样做的目的是提高性能和较少搜索时间,具体可参照treeifyBin()方法。说了这么多,那HashMap的基本原理是怎样的呢?简单粗暴概括一下:

  1、首先判断key是否为Null,如果为null,直接查找Enrty[0],如果不是Null,先计算Key的HashCode,得到Hash值,Hash值是一个int值。

  2、根据Hash值,要找到对应的数组,所以对Entry[]的长度length取模(类似求余的算法,后面详细介绍),得到的就是Entry数组的index。

  3、找到对应的数组就找到了所在的链表,然后按照链表的操作对Value进行插入、删除和查询操作。

  HashMap底层数据结构及存储过程(以上纯属扯淡,下面重点来了):

  JDK1.8之前HashMap底层由数组+链表实现

  JDK1.8之后为了提高效率,底层由数组+链表+红黑树实现

  在创建HashMap集合对象的时候,在JDK1.8之前是在构造方法中创建一个长度为16的Entry[] table来存储键值对,在JDK1.8之后不在构造方法中创建数组了,而是在第一次调用put()方法时创建数组Node[] table 用来存储键值对。

  

  

  假设向哈希表中存储键值对key为zhangsan,value为28,根据zhangsan.hashCode()方法计算出hash值,然后结合数组长度采用取模的算法计算出zhangsan在Node数组中的索引值,如果计算出的索引没有值,则直接将28存储到数组中。那么,取模算法到底是怎样的呢?看下图。

  

  红色框出来的代码告诉我们,采用的是按位与运算计算出索引值,其实就是我们熟知的取余法,但是为什么没有直接使用hash%length直接取余呢,是因为与运算效率更高,与运算规则:相同的二进制数位上都是1时结果为1,否则为0。在某种条件下hash%length等于n-1&hash,什么条件呢?那就是HashMap要求的数组长度length必须为2的n次幂,HashMap的构造函数允许我们自定义数组长度,但是它会检测然后自动帮我们把设置的长度往上转成最近的2的n次幂,比如我们初始化一个HashMap对象,设置数组长度为10,显然10不是2的某次幂,这时候会自动向上转成最近的2的某次幂,也就是16。

HashMap<String,String> hashMap = new HashMap<>(10);

解析源码,彻底弄懂HashMap(持续更新中)的更多相关文章

  1. Android源码编译常见错误(持续更新)

    本文为个人工作中处理遇到的编译问题做个小结,后续遇到新的问题,持续更新. No such file or directory: 1. 检查路径是否有问题,文件是否存在,若文件存在且路径没问题 2. 检 ...

  2. IntelliJ IDEA 2019.2.1 破解教程, 最新激活码(激活到2089年8月,亲测有效,持续更新中...)

    当前最新版本 IDEA 2019.2.1 本来笔者这边是有个正版激活码可以使用的,但是,2019.9月3号的时候,一些小伙伴反映这个注册码已经失效了,于是拿着自己的 IDEA, 赶快测试了一下,果不其 ...

  3. IDEA 2019.2破解激活教程(激活到2089年8月,亲测有效,持续更新中...)

    本来笔者这边是有个正版激活码可以使用的,但是,2019.9月3号的时候,一些小伙伴反映这个注册码已经失效了,于是拿着自己的 IDEA, 赶快测试了一下,果不其然,已然是不能用了. 好在,笔者又找到了新 ...

  4. Flink 源码解析 —— 源码编译运行

    更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...

  5. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  6. 【JDK1.8】 Java小白的源码学习系列:HashMap

    目录 Java小白的源码学习系列:HashMap 官方文档解读 基本数据结构 基本源码解读 基本成员变量 构造器 巧妙的tableSizeFor put方法 巧妙的hash方法 JDK1.8的putV ...

  7. Apache源码包在LINUX(CENTOS6.8)中的安装(出现问题及解决)

    任务:在CENT6.8系统中安装Apache(版本为:httpd-2.4.41) 前提:由于源码包必须先编译后安装,所以必须先安装编译器:gcc 理论步骤: 1.检测gcc软件包,如果不存在则进行安装 ...

  8. 直播平台源码搭建教程:微信小程序中的直播如何去掉水印

    直播平台源码搭建教程:微信小程序中的直播如何去掉水印 本文与大家分享一下直播平台源码搭建教程,如何去掉直播视频的水印 var services = require('../../lib/service ...

  9. fastadmin 后台管理框架使用技巧(持续更新中)

    fastadmin 后台管理框架使用技巧(持续更新中) FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架,具体介绍,请查看文档,文档地址为:https://doc. ...

  10. 2020年腾讯实习生C++面试题&持续更新中(5)

    2020年腾讯实习生C++面试题&持续更新中(5) 大家好呀,我是好好学习天天编程的天天~ 昨天一位小伙伴反馈已经拿到了腾讯offer,很是替小伙伴的激动~ 那今天还是持续给大家分享面经,希望 ...

随机推荐

  1. 在 mac osx 上安装OpenOffice并以服务的方式启动

    OpenOffice是Apache基金会旗下的一款先进的开源办公软件套件,包含文本文档.电子表格.演示文稿.绘图.数据库等.包含Microsoft office所有功能.它不仅可以作为桌面应用供普通用 ...

  2. 主成分分析(PCA)模型概述

    数据降维 降维是对数据高维度特征的一种预处理方法.降维是将高维度的数据保留下最重要的一些特征,去除噪声和不重要的特征,从而实现提升数据处理速度的目的.在实际的生产和应用中,降维在一定信息损失范围内,可 ...

  3. 谷歌Waymo估值700亿:自动驾驶迎来春天,但前路漫漫

    在经过近一年的法庭之争后,Waymo与Uber的自动驾驶专利权诉讼案于近日宣布和解.最终的结果,是Uber向Waymo支付0.34%股权(目前价值2.44亿美元).但事实上,与Uber的官司解决后,一 ...

  4. 使用js闭包的好处

    使用闭包有以下几大好处: a:希望一个变量长期驻扎在内存中. b:避免全局变量的污染.

  5. react-native start 启动错误解决方法

    ERROR Error watching file for changes: EMFILE {"code":"EMFILE","errno" ...

  6. 创建git密钥

    前言 git使用https协议,每次pull,push都要输入密码,使用git协议,使用ssh秘钥,可以省去每次输密码 大概需要三个步骤: 一.本地生成密钥对: 二.设置github上的公钥: 三.修 ...

  7. C++扬帆远航——16(猜数字)

    /* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:guessnum.cpp * 作者:常轩 * 微信公众号:Wor ...

  8. 带你学习ES5中新增的方法

    1. ES5中新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括以下几个方面 数组方法 字符串方法 对象方法 2. 数组方法 迭代遍历方法:forEach().map().filter( ...

  9. koa01

    1.koa简介 koa是express团队开发的一个更加轻量级的服务端开发框架,也是未来的趋势 2.安装 npm i -g koa-generator //全局安装koa脚手架 3.创建项目 koa2 ...

  10. [CSS]CSS Position 详解

    一. CSS position 属性介绍 CSS中position属性指定一个元素(静态的,相对的,绝对或固定)的定位方法的类型.有static,relative,absolute和fixed四种取值 ...