Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~
5.5k stars 779 forks source link

2019-03-29:HashMap 的实现原理? #16

Open Moosphan opened 5 years ago

Moosphan commented 5 years ago

HashMap 实际上是一个“链表散列”的数据结构,即数组和链表的结合体。它是基于哈希表的 Map 接口的非同步实现。

例如我们以下图为例,看一下 HashMap 的内部存储结构: c3d16ff23e8306901065975abd10921a

关于 HashMap 的存取过程,可参照下图:

20180423002750407
①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; 
②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; 
③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; 
④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; 
⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; 
⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。

更多可参考以下文章:

manondidi commented 5 years ago

这题其实很难,hashmap是一个key value 存储的数据结构 jdk8对hashmap做了优化,在长度小于8个时候是链表的数据结构超过8个的时候转换成红黑树

至于红黑树,听说字节跳动面试常问这个问题,一种很复杂的数据结构,我不会

Alex-Cin commented 5 years ago

讲到HashMap, 我简单描述一下会问到的问题哦:
1.. 扩容算法, 时间复杂度是多少, 你了解到它实现的原理吗?
2.. 是先扩容还是优先树化;
3.. 有听说过 ArrayMap吗, 为什么ArrayMap会节省内存; (TIPS 稀疏数组, 包装类占据内存);
4.. 和LinkedHashMap有什么区别, Linked怎么做到的访问顺序问题;
5.. LRUCache了解多少?
6.. 对 ConcurrentHashMap 了解吗?

manondidi commented 5 years ago

什么是红黑树就没几个人会

Petterpx commented 5 years ago

首先说到HashMap,你必须要知道 它是数组加链表构成的。那为什么是呢? 然后要对什么是桶(Socket)了解。 了解它的几个重要属性:负载因子,容量,扩容阈值 然后了解它的put方法,Hashcode的计算方法,及哈希碰撞之后的处理方式,也就是了解链表与红黑树的过程。 然后需要了解它的扩充原理,为什么要扩充两倍。。。 后面就差不多了,哈哈

MicroKibaco commented 5 years ago

HashMap 是应用更加广泛的哈希算表实现,行为上与HashTable一致,主要区别在于与HashMap 不是同步的,支持null键和值等.通常情况下,Hashmap进行 put 或get 操作,可以达到常数时间的性能,所以他是绝大部分利用键值对存取场景的首选,比如,实现一个用户ID 和 用户信息对应的运行时存储结构,HashMap 等其他 Map 都扩展了 AbstractMap,这里面包含了通用方法抽象.不同Map的用途,从类图结构就能体现出来,设计已经体现在不同接口上了,大部分使用Map的场景就是放入,访问或者删除,对顺序没有特别的要求,HashMap在这种情况基本上是最好的选择.HashMap的性能表现非常依赖于哈希码的有效性,请务必掌握hashCode和equals的一些基本约定:

RealMoMo commented 5 years ago

Java中HashMap底层实现原理(JDK1.8)源码分析 https://blog.csdn.net/tuke_tuke/article/details/51588156

kuye910 commented 2 years ago

HashMap

没有synchronized修饰、线程不安全、Key和Value都可以为null

底层实现:数组+链表 在JDK8开始,链表高度如果达到8,数组长度达到64,链表就会变成红黑树,元素会以内部类Node的形式存在;

​ 1、计算Key的hash值,二次hash然后对数组的长度进行取模,对应到数组的下标

​ 2、如果没有出现hash冲突,则直接创建存入Node数组

​ 3、如果产生hash冲突,先进行equal比较,相同就取代该元素,如果不同,则判断链表高度插入链表,链表高度达到8,并且数组长度达到64,则转变成红黑树,低于64则变回链表;