Java,这是一个模仿HashMap的put,get功能的自定义MyHashMap

Java 手写HashMap源码

一,手写源码

这是一个模仿HashMap的put,get功能的自定义的MyHashMap

package cn.wxs.demo;import java.io.Serializable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;class MyHashMap<K, V> extends AbstractMap<K, V>implements Map<K, V>, Cloneable, Serializable {//数组默认长度private static final int DEFAULT_CAPACITY = 16;//负载因子private static final float DEFAULT_LOAD_FACTOR = 0.75f;//在jdk8中 链表转换红黑树阈值8 这里参考jdk11,提前进入红黑树private static final int TREEIFY_THRESHOLD = 6;// 在jdk8中 红黑树退回链表是6 这里参考jdk11,提高转换链表的阈值private static final int UNTREEIFY_THRESHOLD = 8;//数组长度低于64,出现转红黑树的情况,进行扩容,原先的二倍private static final int MIN_TREEIFY_CAPACITY = 64;private static Node[] table; // 存储桶数组private static transient int size; // 键值对数量//用于记录集合被修改的次数。在Java集合类中,如果在迭代器遍历集合时,// 集合被修改了,就会抛出ConcurrentModificationException异常,// 这是因为在遍历时,迭代器会检查modCount的值是否与集合的修改次数相等//,如果不相等就会抛出异常,以此保证遍历的安全性。private static transient int modCount;private transient int threshold; // 扩容阈值private final float loadFactor; // 负载因子private static class Node<K, V> {final int hash; // 哈希值final K key; // 键V value; // 值Node<K, V> next; // 下一个节点Node(int hash, K key, V value, Node<K, V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;}public final K getKey()        { return key; }public final V getValue()      { return value; }public final String toString() { return key + "=" + value; }public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}public final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?,?> e = (Map.Entry<?,?>)o;return Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue());}return false;}}public MyHashMap() {this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);}public MyHashMap(int initialCapacity, float loadFactor) {if (initialCapacity <= 0)throw new IllegalArgumentException("Invalid initial capacity");if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Invalid load factor");table = new Node[initialCapacity];size = 0;this.loadFactor = loadFactor;this.threshold = (int) (initialCapacity * loadFactor);}public V get(Object key) {Node<K, V> node = getNode((K) key);return (node != null) ? node.value : null;}public V put(K key, V value) {if (key == null)putForNullKey(value);elseputNonNullKey(key, value);return value;}private void putNonNullKey(K key, V value) {int hash = hash(key);int index = indexFor(hash, table.length);// 遍历链表,查找是否已存在相同的键for (Node node = table[index]; node != null; node = node.next) {if (node.hash == hash && Objects.equals(node.key, key)) {// 如果找到相同的键,更新对应的值node.value = value;return;}}// 如果没有找到相同的键,将新的键值对添加到链表头部addNode(hash, key, value, index);}private void putForNullKey(V value) {// 对于 null 键,存储在数组的第一个位置for (Node node = table[0]; node != null; node = node.next) {if (node.key == null) {// 如果找到 null 键,更新对应的值node.value = value;return;}}// 如果没有找到 null 键,将新的键值对添加到链表头部addNode(0, null, value, 0);}private void addNode(int hash, K key, V value, int bucketIndex) {// 检查是否需要扩容if (size >= threshold)resize(2 * table.length);// 将新的节点添加到链表头部Node newNode = new Node<>(hash, key, value, table[bucketIndex]);table[bucketIndex] = newNode;size++;modCount++;}private Node<K, V> getNode(K key) {if (key == null)return getNodeForNullKey();int hash = hash(key);int index = indexFor(hash, table.length);// 遍历链表,查找指定的键for (Node node = table[index]; node != null; node = node.next) {if (node.hash == hash && Objects.equals(node.key, key))return node;}return null;}private Node<K, V> getNodeForNullKey() {// 对于 null 键,存储在数组的第一个位置for (Node<K, V> node = table[0]; node != null; node = node.next) {if (node.key == null)return node;}return null;}private int hash(K key) {// 计算键的哈希值return (key == null) ? 0 : key.hashCode();}private int indexFor(int hash, int length) {// 根据哈希值和数组长度计算索引位置return hash & (length - 1);}private void resize(int newCapacity) {Node<K, V>[] oldTable = table;int oldCapacity = oldTable.length;// 检查是否达到容量上限if (oldCapacity >= 1 << 30)threshold = Integer.MAX_VALUE;elsethreshold = (int) (newCapacity * loadFactor);// 创建新的数组,将旧的键值对重新分配到新的数组中Node<K,V>[] newTable = new Node[newCapacity];transfer(oldTable, newTable);table = newTable;}private void transfer(Node<K, V>[] src, Node<K, V>[] dest) {// 遍历旧的数组,将键值对重新分配到新的数组中for (Node<K, V> node : src) {while (node != null) {Node<K, V> next = node.next;int index = indexFor(node.hash, dest.length);node.next = dest[index];dest[index] = node;node = next;}}}@Overridepublic void forEach(BiConsumer<? super K, ? super V> action) {Node[] tab;if (action == null)throw new NullPointerException();if (size > 0 && (tab = table) != null) {int mc = modCount;for (Node kvNode : tab) {for (Node<K, V> e = kvNode; e != null; e = e.next)action.accept(e.key, e.value);}if (modCount != mc)throw new ConcurrentModificationException();}}@Overridepublic MyHashMap<K, V> clone() {try {MyHashMap clone = (MyHashMap) super.clone();// TODO: copy mutable state here, so the clone can't change the internals of the originalreturn clone;} catch (CloneNotSupportedException e) {throw new AssertionError();}}/*** 获取hashmap的存储值数量** @return 存储数量*/public int size() {return size;}public boolean isEmpty() {return size() == 0;}@Overridepublic void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {super.replaceAll(function);}@Overridepublic V putIfAbsent(K key, V value) {return super.putIfAbsent(key, value);}@Overridepublic boolean remove(Object key, Object value) {return super.remove(key, value);}@Overridepublic boolean replace(K key, V oldValue, V newValue) {return super.replace(key, oldValue, newValue);}@Overridepublic V replace(K key, V value) {return super.replace(key, value);}@Overridepublic V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {return super.computeIfAbsent(key, mappingFunction);}@Overridepublic V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {return super.computeIfPresent(key, remappingFunction);}@Overridepublic V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {return super.compute(key, remappingFunction);}@Overridepublic V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {return super.merge(key, value, remappingFunction);}@Overridepublic Set<Entry<K, V>> entrySet() {return null;}@Overridepublic V getOrDefault(Object key, V defaultValue) {return super.getOrDefault(key, defaultValue);}public static void main(String[] args) {Map<String,String> para = new HashMap<>();para.remove("");MyHashMap<String, String> param = new MyHashMap<>();param.put("你好", "你好");param.put("你好1", "你好1");param.put("你好2", "你好2");param.put("你好3", "你好3");param.put("你好4", "你好4");param.put("你好5", "你好4");param.put("你好6", "你好4");param.put("你好7", "你好4");param.put("你好8", "你好4");param.put("你好9", "你好4");param.put("你好10", "你好4");param.put("你好11", "你好4");param.put("你好12", "你好4");param.put("你好13", "你好4");param.put("你好14", "你好4");param.put("你好15", "你好4");param.put("你好16", "你好4");param.put("你好17", "你好4");param.put("你好18", "你好4");param.put("你好20", "你好4");param.put("你好21", "你好4");param.put("你好22", "你好4");param.put("你好23", "你好4");param.put("你好24", "你好4");param.put("你好25", "你好4");param.put("你好26", "你好4");param.put("你好27", "你好4");param.put("你好28", "你好4");param.put("你好29", "你好4");param.put("你好30", "你好4");param.put("你好31", "你好4");param.put("你好32", "你好4");param.forEach((k, v) -> System.out.println(k));System.out.println(param.get("你好4"));System.out.println(param.size());}
}

在这里插入图片描述

二,代码介绍

1.这是一个简单的自定义哈希映射(HashMap)的实现。功能不太完善,代码不太优雅,细节不够好,没有源码的那么好。后面我会慢慢进行优化
2.这段代码实现了一个简化版本的 HashMap,包含了 put 和 get 方法,并使用了数组+链表的存储结构。注意,这里的红黑树部分并未实现,只使用了链表来处理冲突。由于短时间没有办法能按自己的方式写出来全部的功能和底层设计,难度还是比较大的后期我会慢慢的把存储结构改成数组+链表+红黑树的数据结构

请注意,这只是一个简化版本的实现,并没有处理扩容、红黑树转换等复杂的细节。实际的 HashMap 实现要复杂得多。如果你对完整的实现感兴趣,建议你查阅 Java 的 HashMap 源代码,以便更好地理解和学习。

三,下期完善功能

存储结构 数组+链表+红黑树

三,HashMap

HashMap 是 Java 中的一个常用的数据结构,它实现了 Map 接口,并且基于哈希表实现。HashMap 允许存储键值对,并且提供了快速的插入、查找和删除操作。

下面是 HashMap 的一些重要特点和概念:

  • 哈希表HashMap 内部使用了一个数组来存储数据,这个数组被称为哈希表。哈希表的每个元素称为一个桶(bucket),每个桶可以存储一个或多个键值对。通过计算键的哈希值,可以确定键值对在哈希表中的位置。

  • 哈希函数:哈希函数用于将键映射到哈希表中的索引位置。好的哈希函数能够将键均匀地分布在哈希表的不同位置上,以减少冲突的概率。在 HashMap 中,键的 hashCode() 方法被用作哈希函数。

  • 冲突:当两个不同的键通过哈希函数计算得到的索引位置相同时,就发生了冲突。HashMap 使用链表或红黑树来解决冲突。当冲突较少时,使用链表;当链表长度超过一定阈值时,转换为红黑树,以提高查找的效率。

  • 键的唯一性:在 HashMap 中,键是唯一的。如果尝试将一个已经存在的键插入到 HashMap 中,它的值将被更新为新的值。

  • null 键HashMap 允许存储一个键为 null 的键值对。这个键将被存储在哈希表的第一个位置上。

  • 迭代顺序HashMap 不保证键值对的迭代顺序,它通常是不确定的。如果需要有序的键值对集合,可以使用 LinkedHashMap

下面是一些常用的操作方法:

  • put(key, value):向 HashMap 中插入一个键值对。
  • get(key):根据键获取对应的值。
  • remove(key):根据键删除对应的键值对。
  • containsKey(key):检查是否包含指定的键。
  • containsValue(value):检查是否包含指定的值。
  • size():返回 HashMap 中键值对的数量。

需要注意的是,HashMap 是非线程安全的,如果在多线程环境中使用,需要进行适当的同步处理,或者使用线程安全的 ConcurrentHashMap

HashMap 的时间复杂度通常是常数级别的,即 O(1),但在最坏的情况下,可能会达到 O(n),其中 n 是 HashMap 中存储的键值对数量。

希望这个简介对你理解 HashMap 有所帮助!如果你有任何其他问题,请随时提问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SIM初始化流程

ATR ATR(Answer To Reset)&#xff1a;复位应答信号&#xff0c;有SIM卡传输给终端&#xff0c;包括SIM卡自身的一些信息&#xff0c;比如支持的传输速率&#xff0c;传输模式等。 SIM卡的ATR代表"Answer to Reset"&#xff0c;即复位响应。当SIM卡被插入设备中时…

Linux驱动入门 —— 利用引脚号操作GPIO进行LED点灯

目录 一、字符设备驱动程序框架 编写驱动程序的步骤&#xff1a; 对于 LED 驱动&#xff0c;我们想要什么样的接口&#xff1f; LED 驱动能支持多个板子的基础&#xff1a;分层思想 二、Linux驱动如何指向一个GPIO 直接通过寄存器来操作GPIO 利用引脚号操作GPIO IMX6UL…

算法通关村第十八关-黄金挑战回溯困难问题

大家好我是苏麟 , 今天带来几道回溯比较困难的题 . 回溯有很多比较难的问题&#xff0c;这里我们看两个&#xff0c;整体来说这两个只是处理略复杂&#xff0c;还不是最难的问题 . 大纲 IP问题 IP问题 描述 : 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 …

redis:一、面试题常见分类+缓存穿透的定义、解决方案、布隆过滤器的原理和误判现象、面试回答模板

redis面试题常见分类 缓存穿透 定义 缓存穿透是一种现象&#xff0c;引发这种现象的原因大概率是遭到了恶意攻击。具体就是查询一个一定不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致这个数据的每次请求都需要查DB&#xff0c;数据库压力…

# 和 $ 的区别①

# 和 $ 都是为了获取变量的值 # 和 $ 区别 : 使用 # 查询 id 为 1 的内容 如果看不懂代码,就去看<<Mybatis 的操作(结合上文)续集>>,我这里为了简练一点就不多解释了 Select("select * from userInfo where id #{id}")UserInfo selectOne(Integer id…

Hive命令操作

1.命令行模式 1. 获取帮助 --> hive -H 或-help 2. 运行hive语句 --> hive -e "执行语句" 3. 运行hive文件 --> hive –f "执行文件" 4. 定义变量 --> hive –hivevar keyvalue 5. 引用变量 --> ${varname} 2. 交互模式 1. 进入客户端 -…

【UE 材质】切换颜色、纹理时的过渡效果

效果 步骤 1. 新建一个工程&#xff0c;创建Basic关卡 2. 创建一个材质&#xff0c;这里命名为“M_Plane”&#xff0c;打开这个材质&#xff0c;在材质图表中添加如下节点 注意“Noise”节点中的函数选择“Voronoi” 3. 对材质“M_Plane”创建材质实例 4. 在场景中放置一个平…

【SpringBoot】FreeMarker视图渲染

目录 一、FreeMarker 简介 1.1 什么是FreeMarker&#xff1f; 1.2 Freemarker模板组成部分 1.3 为什么要使用FreeMarker 二、Springboot集成FreeMarker 2.1 配置 2.2 数据类型 2.2.1 字符串 2.2.2 数值 2.2.3 布尔值 2.2.4 日期 2.3 常见指令 2.3.2 assign 2.3…

docker compose部署wordpress

准备机器&#xff1a; 192.168.58.151 &#xff08;关闭防火墙和selinux&#xff09; 安装好docker服务 &#xff08;详细参照&#xff1a;http://t.csdnimg.cn/usG0s 中的国内源安装docker&#xff09; 部署wordpress: 创建目录&#xff1a; [rootdocker ~]# mkdir…

docker-compose容器编排(单机一键拉起所有容器)

1、安装docker-compose实验 安装完成 2、yaml文件 &#xff08;1&#xff09;定义 一种直观的、以竖列形式展示序列化数据格式的标记语言&#xff0c;可读性高。类似于json格式&#xff0c;但语法简单 yaml通过缩进表示数据结构&#xff0c;连续的项目用-减号表示 &#x…

【教程】查看GPU与PCIe版本和匹配速率

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] PCIe理论速度对照表 bash脚本 #!/bin/bash# 查找所有 NVIDIA GPU 设备的设备ID及其类型 device_info$(lspci | grep -i nvidia | egrep "VGA compatible controller|3D controller" | awk {print $1, …

C# WPF上位机开发(动态库dll的开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 很多时候&#xff0c;我们并不希望所有的程序都放到一个exe里面。因为这样相当于把所有的风险都放在了一个文件里里面&#xff0c;既不利于程序的升…

完全平方数 C语言xdoj49

问题描述 若一个整数n能表示成某个整数m的平方的形式&#xff0c;则称这个数为完全平方数。写一个程序判断输入的整数是不是完全平方数。 输入说明 输入数据为一个整数n&#xff0c;0<n<10000000。 输出说明 如果n是完全平方数&#xff0c;则输出构成这个完全…

Simple Water Caustic Pattern In Unity ShaderGpaph

shadertoy上有各种神奇的效果&#xff0c;以我的见识根本想象不到这些是怎么弄出来的。 不过不会做至少可以先会用。 这篇文章抓取一个shadertoy的示例以制作一个测试效果。 参考这篇shadertoy&#xff0c;使用自定义节点装填hlsl的noise代码 Shader - Shadertoy BETA 首先使…

洛谷P1722 矩阵Ⅱ——卡特兰数

传送门&#xff1a; P1722 矩阵 II - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1722 用不需要除任何数的公式来求。 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<cstdio> #include<cmath> #includ…

12.13_黑马数据结构与算法笔记Java

目录 098 堆 heapify 3 099 堆 增删替换 100 堆 e01 堆排序 100 堆e02 求数组第k大元素 100 堆e03 求数据流第k大元素 100 堆e04 求数据流中位数1 100 堆e04 求数据流中位数2 100 堆e04 求数据流中位数3 101 二叉树 概述 102 二叉树 深度优先遍历 103 二叉树 前中后…

loki 如何格式化日志

部署 grafana-loki 首先介绍一下如何部署 官方文档&#xff1a;部署 grafana-loki 部署命令 设置集群的存储类&#xff0c;如果有默认可以不设置设置命名空间 helm install loki oci://registry-1.docker.io/bitnamicharts/grafana-loki --set global.storageClasslocal -n …

AWR2243级联(TI文档)

摘要 本应用报告描述了TI的级联毫米波雷达系统。该解决方案基于TI的AWR2243雷达芯片。使用20 GHz的本振输入和输出路径&#xff0c;这些芯片中的几个级联在一起并同步工作。每个AWR2243芯片最多支持4个接收天线和3个发射天线。级联多个这样的芯片允许雷达系统使用更多的接收和发…

对于双显卡电脑,如何分辨现在用的是独立显卡还是集成显卡?

一、问题描述 台式电脑本身自带了集成显卡&#xff0c;然后又购买了一块NVIDIA的独立显卡。 现在&#xff0c;就有疑问了&#xff0c;如何判断你的显示器连接的是独立显卡还是集成显卡呢&#xff1f; 二、NVIDIA双显卡机型 1、在桌面右下角&#xff0c;选择NVIDIA图标&…

YOLOv8改进 | 2023主干篇 | 替换LSKNet遥感目标检测主干 (附代码+修改教程+结构讲解)

一、本文介绍 本文给大家带来的改进内容是LSKNet&#xff08;Large Kernel Selection, LK Selection&#xff09;&#xff0c;其是一种专为遥感目标检测设计的网络架构&#xff0c;其核心思想是动态调整其大的空间感受野&#xff0c;以更好地捕捉遥感场景中不同对象的范围上下…