【转】Java学习---HashMap和HashSet的内部工作机制
【原文】https://www.toutiao.com/i6593863882484220430/
HashMap和HashSet的内部工作机制
HashMap 和 HashSet 内部是如何工作的?散列函数(hashing function)是什么?
HashMap 不仅是一个常用的数据结构,在面试中也是热门话题。
Q1. HashMap 如何存储数据?
A1. 以键/值对(key/value)形式存储。你可以使用键(key)来存、取值。
Q2. HashMap 查询时间的复杂度是怎样的?
A2. 是O(n) = O(k * n)。如果 hashCode() 方法能向下面讨论的那样把数据分散到桶(bucket)中,那么平均是O(1)。
Q3. HashMap 内部是如何存储数据的?
A3. HashMap 使用后台数组(backing array)作为桶,并使用链表(linked list)存储键/值对。
桶的后台数组:如下所示
1)使用键(key)和值(value)将一个对象放入 map 中时,会隐式调用 hashCode() 方法,返回哈希值(hash code value),比如 123。两个不同的键能够返回一样的哈希值。良好的哈希算法(hashing algorithm)能够将数值分散开。在上面的例子中,我们假设 (“John”,01/01/1956) 的键和 (“Peter”, 01/01/1995) 的键返回相同的哈希值,都是123。
2)当返回一个 hashCode,例如是 123,初始的 HashMap 容量为 10,它如何知道存储到后台数组(backing array)的哪个索引(index)呢?HashMap 内部会调用 hash(int ) 和 indexFor(int h, int length) 方法。这被称为哈希函数(hashing function)。
简要解释下这个函数:
hashCode() % capacity
123 % 10 = 3
456 % 10 = 6
这表示,“hashCode = 123”存储在备份数组的索引3上。
容量为 10 的情况下,你可能得到的数字在 0 到 9 之间。
一旦 HashMap 达到容量的 75%,也就是哈希因子(hash factor)默认值 0.75,后台数组(backing array)的容量就会加倍,发生重散列(rehashing)为新的 20 的容量重新分配桶。
hashCode() % capacity
123 % 20 = 3
456 % 20 = 16
上面重散列的取模方法有一个缺陷。如果 hashCode 是负数会怎样?负索引可不是你想要的。因此,一个改进的哈希公式会移出符号位,然后再用取模(即 %)运算符计算剩余部分。
(123 & 0x7FFFFFFF) % 20 = 3
(456 & 0x7FFFFFFF) % 20 = 16
这确保你得到的索引值为正数。如果你查看 Java 8 的 HashMap 源码,它的实现使用以下方法:
a). 通过只抽取重要的低位,来防止不良离散值(poorer hashes)。
b). 根据哈希码(hashCode)和容量(capacity),来决定索引(index)。
实际的名称值对(name value pairs)作为一个键/值对存储在 LinkedList 中。
如上图所示,键/值对以链表形式存储。两个不同的键可以产生一样的 hashCode,例如123,并存储在同一个 bucket 中,理解这点至关重要。例如,上面例子中的 “John, 01/01/1956” 和 “Peter, 01/01/1995“ 。你如何只检索 “John, 01/01/1956” 呢?此时你的 key 所属类的 equals() 方法会被调用。它遍历 bucket 为 “123” 的 LinkedList 中的每个条目,使用 equals() 方法找到并检索出键为 “John, 01/01/1956” 的条目。这就是在你的类中实现 hashCode() 和 equals() 方法重要性的原因。如果你使用一个现有的包装类,如 Integer 或 String 作为键,它们已经实现了这两个方法。如果你使用自己写的类作为键,如 “John, 01/01/1956” 这样含有名字和出生日期属性的“MyKey”,你有责任正确地实现这些方法。
Q5. 为什么恰当地设置 HashMap 的初始容量(initial capacity)是最佳实践?
A5. 这样可以减少重散列的发生。
Q6. HashSet 内部如何存储数据?
A6. HashSet 内部使用 HashMap 。它将元素存储为键和值。(译者注:HashSet 把存储的值作为 key)
Q7. 为 Object 实现了一个糟糕的 hashcode() 会有什么影响?
A7. 不同的对象调用 hashCode() 方法应该返回不同的值。如果不同的对象返回相同的值,会导致更多的键/值对存储在同一个 bucket 中。这会降低 HashMap 和 HashSet 的性能。
【转】Java学习---HashMap和HashSet的内部工作机制的更多相关文章
- spring 内部工作机制(二)
本章节讲Spring容器从加载配置文件到创建出一个完整Bean的作业流程及参与的角色. Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表, ...
- Spring容器技术内幕之内部工作机制
引言 Spring容器就像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式工作.如果将Spring容器比作一辆车,那么可以将BeanFactory看成汽车的发动机,而A ...
- [Java语言] HashMap,HashSet,Hashtable,Vector,ArrayList 的关系 <转>
这么几个比较常用的但是比较容易混淆的概念同出于 java.util 包.本文仅作几个类的浅度解析. (本文基于JDK1.7,源码来自openjdk1.7.) ├── Collection │ ├── ...
- Java学习——HashMap
遍历 Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map. ...
- Java学习----HashMap原理
1.HashMap的数据结构 数组的特点是:寻址容易,插入和删除困难:而链表的特点是:寻址困难,插入和删除容易.那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的, ...
- 【转】Java学习---HashMap的工作原理
[原文]https://www.toutiao.com/i6592560649652404744/ HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都 ...
- 基于Java的HashMap和HashSet实现
一.Map接口类: import java.util.Iterator; public interface IMap<K, V> { /* 清除所有键值对 */ void clear(); ...
- 刷题upupup【Java中HashMap、HashSet用法总结】
HashMap: 常用操作 1. containsKey() 判断HashMap是否包含key 2. containsValue() 判断HashMap是否包含“值为value”的元素 3. get( ...
- java遍历hashMap、hashSet、Hashtable
一.遍历HashMap Map<Integer, String> map = new HashMap<Integer, String>(); 方法一:效率高 for(Entry ...
随机推荐
- SpringBoot入门 (八) Cache使用
本文记录学习在SpringBoot中使用Cache. 一 为什么要使用缓存 缓存是一个数据交换的缓冲区,在一些条件下可以替代数据库.举个例子:我们有一个查询的业务,访问数据的频率特别高,且每次访问时的 ...
- Golang 并发concurrency
并发concurrency 很多人都是冲着Go大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码解析来看,goroutine只是由官方实现的超级"线程池"而已.不过话说回来,每个实例4 ...
- layer插件学习——询问框
本文是自己整理的关于layer插件的询问框样式结果 一.准备工作 下载jQuery插件和layer插件,并引入插件(注意:jQuery插件必须在layer插件之前引用) 百度云资源链接: jQuery ...
- ffplay源码分析4-音视频同步
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10307089.html ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg ...
- js 之 this的用法
该篇文章混合了比较多文章,由于自己也水平有限,大家就将就着看下吧,详情可以参看<JavaScript语言精粹>,不过文章提供了很多例子,供大家参阅思考. 首先关于this我想说一句话,这句 ...
- 异步消息队列Celery
Celery是异步消息队列, 可以在很多场景下进行灵活的应用.消息中包含了执行任务所需的的参数,用于启动任务执行, suoy所以消息队列也可以称作 在web应用开发中, 用户触发的某些事件需要较长事件 ...
- MVC母版页_Layout.cshtml
记得在asp.net时候,我们常会使用母版页MasterPage.而在MVC也有此功能,应用起来比MasterPage更加便捷. 在本次练习之前,所有MVC的练习均是创建单独的网页.Insus.NET ...
- Docker初次使用与安装过程
Docker入门 Docker 简介 Docker有两个版本: 社区版(CE) 企业版(EE) Docker Community Edition(CE)非常适合希望开始使用Docker并尝试使用基于容 ...
- 批量导出VBA工程中的Source
在做Excel宏相关项目的开发和维护过程中,我们经常需要导出VBA中的Source,但是Excel提供的宏编辑器中只能一个文件一个文件地导出,很不方便. 下面介绍2种批量导出的方法: 1.Source ...
- 多线程系列(2)线程池ThreadPool
上一篇文章我们总结了多线程最基础的知识点Thread,我们知道了如何开启一个新的异步线程去做一些事情.可是当我们要开启很多线程的时候,如果仍然使用Thread我们需要去管理每一个线程的启动,挂起和终止 ...