快速实现一个简单阉割版的HashMap
简单实现一个底层数据结构为数组 + 链表的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.运行结果

快速实现一个简单阉割版的HashMap的更多相关文章
- 【Head First Servlets and JSP】笔记6:什么是响应首部 & 快速搭建一个简单的测试环境
搭建简单的测试环境 什么是响应首部 最简单的响应首部——Content-Type 设置响应首部 请求重定向与响应首部 在浏览器中查看Response Headers 1.先快速搭建一个简单的测试环境, ...
- 实现一个简单的散列表(HashMap)
下面参考java.util.HashMap<K, V>,写了一个简单的散列表,只实现了其中的put和get方法,使用链接法"碰撞冲突".代码最后,自定义了一个Peopl ...
- 快速构建一个简单的单页vue应用
技术栈 vue-cli webpack vux,vux-loader less,less-loader vue-jsonp vue-scroller ES6 vue-cli:一个vue脚手架工具,利用 ...
- 使用MicroService4Net 快速创建一个简单的微服务
“微服务架构(Microservice Architecture)”一词在过去几年里广泛的传播,它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务.目前,这种架构方式还没有准确的定义,但是 ...
- Objective-C ,ios,iphone开发基础:快速实现一个简单的图片查看器
新建一个single view 工程: 关闭ARC , 在.xib视图文件上拖放一个UIImageView 两个UIButton ,一个UISlider ,布局如图. 并为他们连线, UIImage ...
- 快速设计一个简单的WPF串口上位机
最近一直在学习UWP,其中有的技术参考了WPF,所以又回头再来学习WPF,感觉学的东西很杂,必须记录一下,不然时间长了还得忘掉,于是申请开始写博客,将学习的心得记录一下,以备后用.这次是因为公司内训, ...
- hdu 1757 (矩阵快速幂) 一个简单的问题 一个简单的开始
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757 题意不难理解,当x小于10的时候,数列f(x)=x,当x大于等于10的时候f(x) = a0 * ...
- 用 PyQt5 快速构建一个简单的 GUI 应用
1. 介绍 Python GUI 常用的 3 种框架是:Tkinter.wxpython.PyQt5 PyQt5 基于 Qt,是 Python 和 Qt 的结合体,可以用 Python 语言编写跨平台 ...
- Python3-在windows快速运行一个简单的本地 HTTP 服务器
1.打开控制台2.python -m http.server
- IDEA快速创建一个简单的SpringBoot项目(需要联网)
一.点击File-New-Project,选择Spring initializr ,选择jdk1.8及以上 二.填写相关信息,点击Next 3.选择Web -Spring Web,点击Next 4.输 ...
随机推荐
- UML建模语言、设计原则、设计模式
1.UML统一建模语言 定义:用于软件系统设计与分析的语言工具 目的:帮助开发人员更好的梳理逻辑.思路 学习地址:UML概述_w3cschool 官网:https://www.omg.org/spec ...
- Golang 实现时间戳和时间的转化
何为时间戳: 时间戳是使用数字签名技术产生的数据,签名的对象包括了原始文件信息.签名参数.签名时间等信息.时间戳系统用来产生和管理时间戳,对签名对象进行数字签名产生时间戳,以证明原始文件在签名时间之前 ...
- IO多路复用的理解/演变过程
目录 阻塞IO 非阻塞 IO select epoll 总结一下. 阻塞IO 服务端为了处理客户端的连接和请求的数据,写了如下代码. listenfd = socket(); // 打开一个网络通信端 ...
- 【题解】CF374C Inna and Dima
题面传送门 解决思路 本题是找最长路的图上问题,所以先考虑如何建图. 首先把每一个字母转化为数字,然后对于每一个点枚举四个方向,如果有下一个字母,就向那个点建一条边,可以用 \(vector\) 存图 ...
- 陪你去看 Lodash.js 起步
lodash 起步(数组) Lodash 是一个较为流行的 JavaScript 的实用工具库. 在开发过程中如果能熟练使用一些工具库提供的方法,有利于提高开发效率. 笔者从 API 上入手,不分析其 ...
- 嵌入式-C语言基础:数组指针
定义一个数组指针,指向二维数组: int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int (*p)[4];//定义二维指针数组 p=a;//指向二维数组 ...
- c# 使用委托子窗体改变父窗体控件
首先创建两个窗体,在窗体1和窗体2放上对应的控件 在窗体1的代码如下 using System; using System.Collections.Generic; using System.Comp ...
- win7修改开机动画
开机动画的修改 首先win7的过场动画是存在于C:\Windows\System32\bootres.dll ,而修改过程动画就需要修改这个dll,我不会改,所以只能用工具美化大师,软媒魔方里面的一个 ...
- Java外包程序员的技术出路
学习的两个目的: 应付面试 应付工作(解决问题) 首先要明白学习的目的,不同阶段,不同技术的学习目的是不一样的. 有些技术,仅仅是应用级别的,有些技术是原理级别的(主要还是应试).所以不同技术.不同时 ...
- Kafka教程(一)基础入门:基本概念、安装部署、运维监控、命令行使用
Kafka教程(一)基础入门 1.基本概念 背景 领英->Apache 分布式.消息发布订阅系统 角色 存储系统 消息系统 流处理平台-Kafka Streami ...