简单实现一个底层数据结构为数组 + 链表的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的更多相关文章

  1. 【Head First Servlets and JSP】笔记6:什么是响应首部 & 快速搭建一个简单的测试环境

    搭建简单的测试环境 什么是响应首部 最简单的响应首部——Content-Type 设置响应首部 请求重定向与响应首部 在浏览器中查看Response Headers 1.先快速搭建一个简单的测试环境, ...

  2. 实现一个简单的散列表(HashMap)

    下面参考java.util.HashMap<K, V>,写了一个简单的散列表,只实现了其中的put和get方法,使用链接法"碰撞冲突".代码最后,自定义了一个Peopl ...

  3. 快速构建一个简单的单页vue应用

    技术栈 vue-cli webpack vux,vux-loader less,less-loader vue-jsonp vue-scroller ES6 vue-cli:一个vue脚手架工具,利用 ...

  4. 使用MicroService4Net 快速创建一个简单的微服务

    “微服务架构(Microservice Architecture)”一词在过去几年里广泛的传播,它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务.目前,这种架构方式还没有准确的定义,但是 ...

  5. Objective-C ,ios,iphone开发基础:快速实现一个简单的图片查看器

    新建一个single view 工程: 关闭ARC , 在.xib视图文件上拖放一个UIImageView  两个UIButton ,一个UISlider ,布局如图. 并为他们连线, UIImage ...

  6. 快速设计一个简单的WPF串口上位机

    最近一直在学习UWP,其中有的技术参考了WPF,所以又回头再来学习WPF,感觉学的东西很杂,必须记录一下,不然时间长了还得忘掉,于是申请开始写博客,将学习的心得记录一下,以备后用.这次是因为公司内训, ...

  7. hdu 1757 (矩阵快速幂) 一个简单的问题 一个简单的开始

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1757 题意不难理解,当x小于10的时候,数列f(x)=x,当x大于等于10的时候f(x) = a0 * ...

  8. 用 PyQt5 快速构建一个简单的 GUI 应用

    1. 介绍 Python GUI 常用的 3 种框架是:Tkinter.wxpython.PyQt5 PyQt5 基于 Qt,是 Python 和 Qt 的结合体,可以用 Python 语言编写跨平台 ...

  9. Python3-在windows快速运行一个简单的本地 HTTP 服务器

    1.打开控制台2.python -m http.server

  10. IDEA快速创建一个简单的SpringBoot项目(需要联网)

    一.点击File-New-Project,选择Spring initializr ,选择jdk1.8及以上 二.填写相关信息,点击Next 3.选择Web -Spring Web,点击Next 4.输 ...

随机推荐

  1. C语言白盒测试讲义

    好久没有做过C语言的白盒测试了,估计以后也没这个机会.把自己之前参加过的培训素材做个分享. 素材下载链接:https://pan.baidu.com/s/1LPD9Az04zEj8RuCICaKYxQ ...

  2. CH58X/CH57X/V208 Observer(观察者)例程讨论讲解

    使用的是沁恒的CH582M的Observer例程与官方的demo板. 本例程的功能是主机扫描到从机的MAC地址并打印出来. 先对宏定义进行理解讨论. 最大响应扫描数为8,在串口调试助手那里可以看到打印 ...

  3. 【题解】CF1659E AND-MEX Walk

    题目传送门 位运算 设题目中序列 \(w_1,w_1 \& w_2,w_1 \& w_2 \& w_3,\dots,w_1 \& w_2 \& \dots \& ...

  4. 从 WinDbg 角度理解 .NET7 的AOT玩法

    一:背景 1.讲故事 前几天 B 站上有位朋友让我从高级调试的角度来解读下 .NET7 新出来的 AOT,毕竟这东西是新的,所以这一篇我就简单摸索一下. 二:AOT 的几个问题 1. 如何在 .NET ...

  5. 2022-11-04 Acwing每日一题

    本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的.同时也希望 ...

  6. CPU TLB原理 [转载好文]

    首先,我们知道MMU的作用是把虚拟地址转换成物理地址.虚拟地址和物理地址的映射关系存储在页表中,而现在页表又是分级的.64位系统常见的配置是4级页表,就以4级页表为例说明.分别是PGD.PUD.PMD ...

  7. 《回炉重造》——Lambda表达式

    前言 Lambda 表达式(Lambda Expression),相信大家对 Lambda 肯定是很熟悉的,毕竟我们数学上经常用到它,即 λ .不过,感觉数学中的 Lambda 和编程语言中的 Lam ...

  8. 解决windows installation failed! Error: 无法访问 Windows Installer 服务

    这种错误,是因为没有开启winodws Installer这个服务导致的,在开始菜单搜索"服务",找到windows Installer 这个服务,右键--属性--把启动类型 选成 ...

  9. Linux deb系统 nginx 配置解析php

    如果你是root用户可以不加sudo 首先安装php php-fpm nginx sudo apt-get install php php-fpm nginx -y nginx 是一个轻量级的http ...

  10. oracle 分析函数——ration_to_report 求占有率(百分比)

    oracle 的分析函数有很多,但是这个函数总是会忘记,我想通过这种方式能让自己记起来,不至于下次还要百度. 创表.表数据(平时练手的表): prompt PL/SQL Developer impor ...