

快速实现一个简单阉割版的HashMap - 帝莘
source link: https://www.cnblogs.com/zaevn00001/p/17099630.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

简单实现一个底层数据结构为数组 + 链表的HashMap,不考虑链表长度超过8个时变为红黑树的情况。
1.示例图

2.分析需求
- put数据时:
- key值hash后的索引处没有元素,需要创建链表头节点,放到该位置的数组空间里。
- key值hash后的索引处有元素,说明产生Hash碰撞,需要在链表中结尾处挂载节点,如果在遍历链表的过程中,发现了同key的数据,则执行覆盖即可,不再继续往下遍历去挂载新节点。
- 假设数组使用的空间超过了总长度的75%,那么对数组进行扩容。先创建新数组,把旧数据写到新数组中(此时需要重新根据key计算Hash,因为数据长度变化了,影响计算结果了),在用新数据替换掉原来的旧数组。
- get数据时:
- key值hash后的索引下标处的元素为空的话,则不存在数据。
- key值hash后的索引下标处存在链表的话,需要遍历链表,找到key相对应的value值。
3.代码实现
-
Node类实现
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:25 */ public class Node { String key; String value; Node next; public Node(String key, String value, Node nextNode) { this.key = key; this.value = value; this.next = nextNode; } }
-
LinkNode类实现
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:27 */ public class ListNode { // 头节点 Node head; /** * 添加数据,挂载链表的节点 * @param key * @param value */ public void addNode(String key,String value){ // 如果头节点是空,则结束 if(head == null ){return;} // 如果头节点不为空,则往下挂载节点 Node node = new Node(key,value,null); Node temp = head; while(true){ // 遇到相同的key,覆盖数据 if(key.equals(temp.key)){ temp.value = value; return; } if(temp.next == null){ break; } temp = temp.next; } // 循环结束后则挂上数据 temp.next = node; } /** * 获取数据 * @param key * @return */ public String getNode(String key){ if(head == null ){return null;} Node temp = head; while(true){ if(key.equals(temp.key)){ return temp.value; } if(temp.next == null){ break; } temp = temp.next; } return null; } }
-
MyHashMap类实现
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:27 */ public class MyHashMap { // 数组初始化:2的n次方 ListNode[] map = new ListNode[8]; // ListNode的个数 int size; // 由于扩容时是先创建一个新数组,因此先声明出来 ListNode[] mapNew; int sizeNew; /** * put方法 * @param key * @param value */ public void put(String key,String value){ if(size>map.length * 0.75){ System.out.println("开始进行扩容,当前size="+size+",数组长度为:"+map.length); doExtendMap(); System.out.println("扩容结束,当前size="+size+",数组长度为:"+map.length); } // 1.对key进行hash算法然后取模 int index = Math.abs(key.hashCode())%map.length; ListNode listNode = map[index]; // 如果索引位置的元素为空,则新加一个元素(创建头节点) if(listNode == null){ ListNode listNodeNew = new ListNode(); Node node = new Node(key,value,null); listNodeNew.head = node; map[index] = listNodeNew; size ++; }else{ // 如果索引位置的元素不为空,则往链表中挂载数据 listNode.addNode(key,value); } } public String get(String key){ // 1.对key进行hash算法然后取模 int index = Math.abs(key.hashCode())%map.length; if(map[index] == null){ return null; }else{ return map[index].getNode(key); } } /** * 达到阈值后开始进行扩容 */ public void doExtendMap(){ sizeNew = 0; // 1.先创建一个新的数组,长度为原来的二倍 mapNew = new ListNode[map.length * 2]; // 2.将旧数据映射到新的数组上(因为数组长度变化,因此hash规则变化,所有的值需要重新计算hash值) for(int i = 0;i<map.length;i++){ ListNode listNode = map[i]; if(listNode == null){ continue; } Node temp = listNode.head; while (true){ doPutData(mapNew,temp.key,temp.value); if(temp.next == null){ break; } temp = temp.next; } } // 3.将新的数组替换旧的数组 map = mapNew; this.size = sizeNew; } private void doPutData(ListNode[] mapParam,String key,String value){ int index = Math.abs(key.hashCode())%mapParam.length; ListNode listNode = mapParam[index]; if(listNode == null){ ListNode listNodeNew = new ListNode(); Node node = new Node(key,value,null); listNodeNew.head = node; mapParam[index] = listNodeNew; sizeNew ++; }else{ listNode.addNode(key,value); } } public static void main(String[] args) { // 1、一般校验 MyHashMap hashMap0=new MyHashMap(); hashMap0.put("key1","value1"); System.out.println("一般校验:"+hashMap0.get("key1")); System.out.println("--------------------------------------------"); // 2、同key覆盖校验 MyHashMap hashMap1=new MyHashMap(); hashMap1.put("key2","value00"); hashMap1.put("key2","value01"); System.out.println("同key覆盖校验:"+hashMap1.get("key2")); System.out.println("--------------------------------------------"); // 3、哈希碰撞校验(k1和k9的经过哈希计算后得到的索引都是6) MyHashMap hashMap2=new MyHashMap(); hashMap2.put("k1","value_k1"); hashMap2.put("k9","value_k9"); System.out.println("哈希碰撞校验:k1:"+hashMap2.get("k1")+" k9:"+hashMap2.get("k9")); System.out.println("--------------------------------------------"); // 4、扩容校验 MyHashMap hashMap3=new MyHashMap(); hashMap3.put("m3","cccccc"); hashMap3.put("c1","kkkkkk"); hashMap3.put("c2","mmmmmmm"); hashMap3.put("b1","bbbbbbb"); hashMap3.put("m1","cccccc"); hashMap3.put("c3","kkkkkk"); hashMap3.put("c4","mmmmmmm"); hashMap3.put("b2","bbbbbbb"); hashMap3.put("m2","cccccc"); hashMap3.put("c5","kkkkkk"); hashMap3.put("c6","mmmmmmm"); hashMap3.put("b3","bbbbbbb"); System.out.println("扩容后的c4:"+hashMap3.get("c4")); System.out.println("扩容后的b3:"+hashMap3.get("b3")); } }
3.运行结果

Recommend
-
61
新浪科技讯北京时间9月13日凌晨消息,苹果公司今日在ApplePark乔布斯剧院举行秋季发布会,发布iPhone8和AppleWatch3等产品。iPhone8规格共有4.7英寸和5.5英寸Plus两个版本。硬件方面,iPhone8采用目前
-
98
青春阉割手册44岁那年,冯小刚在自传中写下如今中年男子们三缄其口的心声——对年轻异性的巨大陶醉感——“她的长相我已经记不清了,印象最深的是她的脖子十分的光洁,光洁的颈部优美地立在军装的小翻领中。洗完澡,披着湿漉漉的头发,光着脖子空...
-
18
(原标题:谁是真的全网通5G 手机 ?买前必看,否则后悔) 此次试验频率许可,标志着中国广电在相关地区正式获得5G频率使用权,...
-
13
HashMap简介 HashMap是Java中一中非常常用的数据结构,也基本是面试中的“必考题”。它实现了基于“K-V”形式的键值对的高效存取。JDK1.7之前,HashMap是基于数组+链表实现的,1.8以后,HashMap的底层实现中加入了红黑树用于提升查...
-
8
V2EX › 程序员 小米的频段阉割,实际上是软屏蔽? baobao1270 ·
-
8
Chrome 阉割指南 Posted 2021-12-28 | stdout 不折腾不舒服。继之前 Edge调教 之后记录一下 Chrome V70怎么阉割。
-
10
08年奥运「黑客」今何在? 本文...
-
18
千元亮机卡实锤!AMD RX 6300来了:阉割得不像样|amd|处理器|显卡|rx_网易订阅 日前有传闻称,AMD在准备一款入门级的桌面显卡RX 6300,在已有RX 6400的基础上...
-
2
详细了解JVM运行时内存 1.程序计数器 程序计数器也叫作PC寄存器,是一块很小的内存区域,可以看做是当前线程执行的字节码的行号指示器。字节码的解释工作就是通过改变程序计数器里面的值来获得下一...
-
1
服务架构进化论 原始分布式时代 一直以来,我可能和大多数的人认知一样,认为我们的服务架构的源头是单体架构,其实不然,早在单体系统盛行之前,我们的前辈们就已经探索过使用多个独立的分布式...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK