美团面试题:Hashmap的结构,1.7和1.8有哪些区别,深入的分析

发布时间:2026/10/6 12:55:10

美团面试题:Hashmap的结构,1.7和1.8有哪些区别,深入的分析 一 真实面试题之Hashmap的结构1.7和1.8有哪些区别不同点1JDK1.7用的是头插法而JDK1.8及之后使用的都是尾插法那么他们为什么要这样做呢因为JDK1.7是用单链表进行的纵向延伸当采用头插法时会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法能够避免出现逆序且链表死循环的问题。2扩容后数据存储位置的计算方式也不一样1. 在JDK1.7的时候是直接用hash值和需要扩容的二进制数进行这里就是为什么扩容的时候为啥一定必须是2的多少次幂的原因所在因为如果只有2的n次幂的情况时最后一位二进制数才一定是1这样能最大程度减少hash碰撞hash值 length-12、而在JDK1.8的时候直接用了JDK1.7的时候计算的规律也就是扩容前的原始位置扩容的大小值JDK1.8的计算方式而不再是JDK1.7的那种异或的方法。但是这种方式就相当于只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式。在计算hash值的时候JDK1.7用了9次扰动处理4次位运算5次异或而JDK1.8只用了2次扰动处理1次位运算1次异或。扩容流程对比图3JDK1.7的时候使用的是数组 单链表的数据结构。但是在JDK1.8及之后时使用的是数组链表红黑树的数据结构当链表的深度达到8的时候也就是默认阈值就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从On变成OlogN提高了效率这里在重新进行补充两个问题2019-09-031为什么在JDK1.7的时候是先进行扩容后进行插入而在JDK1.8的时候则是先插入后进行扩容的呢其实就是当这个Map中实际插入的键值对的值的大小如果大于这个默认的阈值的时候初始是16*0.7512的时候才会触发容//这个是在JDK1.8中的先插入后扩容 if (size threshold) resize();其实这个问题也是JDK8对HashMap中主要是因为对链表转为红黑树进行的优化因为你插入这个节点的时候有可能是普通链表节点也有可能是红黑树节点但是为什么1.8之后HashMap变为先插入后扩容的原因我也有点不是很理解欢迎来讨论这个问题但是在JDK1.7中的话是先进行扩容后进行插入的就是当你发现你插入的桶是不是为空如果不为空说明存在值就发生了hash冲突那么就必须得扩容但是如果不发生Hash冲突的话说明当前桶是空的后面并没有挂有链表那就等到下一次发生Hash冲突的时候在进行扩容但是当如果以后都没有发生hash冲突产生那么就不会进行扩容了减少了一次无用扩容也减少了内存的使用void addEntry(int hash, K key, V value, int bucketIndex) { //这里当钱数组如果大于等于12假如阈值的话并且当前的数组的Entry数组还不能为空的时候就扩容 if ((size threshold) (null ! table[bucketIndex])) { //扩容数组比较耗时 resize(2 * table.length); hash (null ! key) ? hash(key) : 0; bucketIndex indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { EntryK,V e table[bucketIndex]; //把新加的放在原先在的前面原先的是e现在的是newnext指向e table[bucketIndex] new Entry(hash, key, value, e);//假设现在是new size; }2为什么在JDK1.8中进行对HashMap优化的时候把链表转化为红黑树的阈值是8,而不是7或者不是20呢面试蘑菇街问过如果选择6和8如果链表小于等于6树还原转为链表大于等于8转为树中间有个差值7可以有效防止链表和树频繁转换。假设一下如果设计成链表个数超过8则链表转换成树结构链表个数小于8则树结构转换成链表如果一个HashMap不停的插入、删除元素链表个数在8左右徘徊就会频繁的发生树转链表、链表转树效率会很低。还有一点重要的就是由于treenodes的大小大约是常规节点的两倍因此我们仅在容器包含足够的节点以保证使用时才使用它们当它们变得太小由于移除或调整大小时它们会被转换回普通的node节点容器中节点分布在hash桶中的频率遵循泊松分布桶的长度超过8的概率非常非常小。所以作者应该是根据概率统计而选择了8作为阀值//Java中解释的原因 * Because TreeNodes are about twice the size of regular nodes, we * use them only when bins contain enough nodes to warrant use * (see TREEIFY_THRESHOLD). And when they become too small (due to * removal or resizing) they are converted back to plain bins. In * usages with well-distributed user hashCodes, tree bins are * rarely used. Ideally, under random hashCodes, the frequency of * nodes in bins follows a Poisson distribution * (http://en.wikipedia.org/wiki/Poisson_distribution) with a * parameter of about 0.5 on average for the default resizing * threshold of 0.75, although with a large variance because of * resizing granularity. Ignoring variance, the expected * occurrences of list size k are (exp(-0.5) * pow(0.5, k) / * factorial(k)). The first values are: * * 0: 0.60653066 * 1: 0.30326533 * 2: 0.07581633 * 3: 0.01263606 * 4: 0.00157952 * 5: 0.00015795 * 6: 0.00001316 * 7: 0.00000094 * 8: 0.00000006 * more: less than 1 in ten million二哈希表如何解决Hash冲突三为什么HashMap具备下述特点键-值key-value都允许为空、线程不安全、不保证有序、存储位置随时间变化四为什么 HashMap 中 String、Integer 这样的包装类适合作为 key 键五HashMap 中的 key若 Object类型 则需实现哪些方法六总结与面试要点1. JDK 1.7 与 JDK 1.8 HashMap 核心区别总结下表总结了 JDK 1.7 与 JDK 1.8 中 HashMap 的主要差异对比维度JDK 1.7JDK 1.8数据结构数组 单向链表数组 单向链表 红黑树插入方式头插法新节点插入链表头部尾插法新节点插入链表尾部扩容时机先扩容后插入先插入后扩容hash 计算9次扰动4次位运算 5次异或2次扰动1次位运算 1次异或扩容后位置计算重新计算 hash (newLength-1)利用规律原位置 或 原位置旧容量树化阈值无树化机制链表长度 ≥ 8 且桶容量 ≥ 64 时树化退化阈值无退化机制树节点数 ≤ 6 时退化为链表并发问题头插法可能导致环形链表死循环尾插法减少死循环风险但仍非线程安全2. 高频面试追问方向方向一为什么 HashMap 线程不安全JDK 1.7多线程扩容时头插法可能导致环形链表造成死循环。JDK 1.8虽然改为尾插法避免了环形链表但 put/get 操作未加锁多线程同时修改仍会导致数据覆盖、size 计算不准确等问题。追问点能否举例说明并发 put 如何导致数据丢失ConcurrentHashMap 如何解决这些问题方向二负载因子为什么默认是 0.75空间与时间平衡0.75 是统计学上的一个折中值。负载因子过小如 0.5会导致频繁扩容空间利用率低负载因子过大如 0.9会导致哈希冲突增加链表变长查询效率下降。数学依据根据泊松分布当负载因子为 0.75 时哈希冲突的概率相对较低同时空间利用率较高。追问点如果已知数据量固定如何设置初始容量和负载因子以优化性能方向三为什么树化阈值是 8退化阈值是 6树化阈值 8基于泊松分布链表长度达到 8 的概率极低约 0.00000006。超过 8 时红黑树的 O(log n) 性能优势开始显现且 TreeNode 大小约为 Node 的两倍仅在必要时才转换。退化阈值 6设置一个缓冲区间7避免频繁的树-链表转换。如果在 8 附近频繁插入删除阈值 6 和 8 的差值可以防止反复转换带来的性能开销。追问点为什么不用 AVL 树而用红黑树红黑树在 HashMap 中的优势是什么3. 面试实战建议结合源码回答能说出关键方法的名称和大致逻辑如 resize()、putVal()、treeifyBin()。对比记忆将 1.7 和 1.8 的区别归纳成表格便于清晰表达。延伸思考除了上述区别还可以准备「为什么 HashMap 容量总是 2 的幂次方」、「hash 方法的具体实现」、「多线程下如何安全使用 HashMap」等延伸问题。掌握这些核心区别和追问方向不仅能应对基础面试题还能在深度追问中展现对 HashMap 设计哲学的深入理解。
延伸阅读

更多相关文章

2026/10/6 4:19:10

Jmeter之函数助手

函数助手:通过里面函数的返回值作为参数 一、__counter 1.功能 每次调用计数器函数都会产生一个新值,从1开始每次加1。支持多线程(多用户):计数器既可以被配置成针对每个虚拟用户是独立的,也可以被配置成所…

2026/10/6 11:15:12

Max抢票机器人:零基础3步配置到全自动抢票的教程

Max抢票机器人:零基础3步配置到全自动抢票的教程 【免费下载链接】tix_bot Max搶票機器人(maxbot) help you quickly buy your tickets 项目地址: https://gitcode.com/gh_mirrors/ti/tix_bot 🎯 一句话说清:这个抢票机器人能干什么 …

2026/10/7 5:50:20

Prompt工程实战:从提示词优化到RAG与参数调优的完整指南

前段时间我在做一个内部文档问答的小工具,初版效果很一般。模型能看懂文档,但回答总是"正确的废话"——不引用原文、不区分"文档里没有的信息",甚至会在总结时自带偏见。后来我花了半天时间把每条提示词拆开重新设计&…

2026/10/7 5:50:20

从RAG到Agent:Chatbot联网搜索架构演进与工程落地

做聊天机器人做久了,你会反复撞到同一堵墙:模型再聪明,它也不知道今天几点下雨、刚刚发布的行业新闻、或者你司内部那条最新的工单状态。知识截止日期就像一堵砖墙,模型的所有认知都冻结在训练结束的那一刻。所以“联网搜索”这四…

2026/10/7 5:50:20

Windows Low Integrity Level与文件ACL协同机制解析

1. 项目概述:一次深夜排查揭示的 Windows 权限底层机制“我被 deepseek harness 的一个 bug 折腾到了凌晨 2 点”——这句话不是情绪宣泄,而是典型的企业级本地 AI 工具链在 Windows 环境落地时遭遇的真实困境。它背后牵扯的不是某行 Python 代码写错了&…

2026/10/7 5:50:20

尤克里里新手必看:从选琴到指弹的完整资源清单与避坑指南

一把四根弦的小琴为什么能让人上头?我身边不少朋友刷了几个弹唱视频之后冲动下单,然后琴在墙角吃灰半年。问就是“和弦按不响”“扫弦像杀鸡”“谱子找不到”。说实话,尤克里里资源这块,网上零零碎碎的东西特别多,但真…

2026/10/7 5:50:20

GaN与SiC选型实战:宽禁带半导体应用场景与设计要点

1. 宽禁带半导体的两条路线之争功率半导体圈子里,氮化镓和碳化硅的讨论热度这几年一直没降过。但凡参加一场电源技术研讨会,或者翻一翻充电头拆解报告,这两个词必然反复出现。很多刚入行的朋友会直接问:到底该学哪个?选…

2026/10/7 5:45:19

水质检测系统全栈开发实战:从数据库设计到前后端数据闭环

简介:这是一套基于Java、JavaScript与HTML共同实现的水质检测系统完整项目包,面向高校毕业设计、课程设计以及个人项目开发场景,可帮助学习者快速掌握Java Web项目从后端逻辑到前端展示的完整流程。项目涵盖用户管理、水质数据录入与查询、检…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑