如何写一个自己的HashMap
想必很多Java工程师出去面试的时候都会被问到HashMap的底层实现原理,很多人觉得没什么必要,反正我会用就行,就我的感觉而言,在初期确实没什么必要,但是站在公司角度想,如果面试者连底层实现都搞定了,还怕一点表层应用的东西吗?又或者说,我看到了另一个答案最能打动我:沉下去有多深,浮上来就有多高。
开始说一下必要的准备阶段:
1.在写自己的HashMap之初,我们需要对一定HashMap有一个初步的认识,比如HashMap底层是由数组和链表实现的,具体的结构图如图所示:
还有比如说最大容量,初始容量,加载因子,红黑树等等概念
推荐几个具体点的地址:
A.微信公众号 --- 码农翻身,搜索HashMap就有
B.https://mubu.com/edit/mY8GnK94oi 幕布思维导图
C.网易云课堂搜索HashMap,有一门只要一分钱的课程,40多分钟,看完也可以有一个比较明确的了解
2.开始撸代码了
A.首先定义一个自己的Map接口,和链表接口:
public interface MyMapInterface<k,v> {
// 大小
int size();
// 是否为空
boolean isEmpty();
// 根据key获取元素
Object get(Object key);
// 添加元素
Object put(Object key,Object value);
// 内部接口
interface Entry<k,v>{
k getKey();
v getValue();
}
}
B.编写实现类:
public class MyHashMap<k,v> implements MyMapInterface {
// 初始容量大小 --- 源码写法 1 << 4
private final int DEFAULT_INITIAL_CAPACITY = 16;
// 加载因子
private final float DEFAULT_LOAD_FACTOR = 0.75f;
// 根据定义的静态内部类,初始化链表,长度为默认长度
Node[] table = new Node[DEFAULT_INITIAL_CAPACITY];
// 长度
private int size = 0;
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public Object put(Object key, Object value) {
// 计算key的hash值
int hashValue = hash(key);
// 计算出应该存放的位置
int i = indexFor(hashValue,table.length);
// 如果i处有数据且key一样,进行覆盖
for(Node node = table[i];node != null; node = node.next){
Object k;
if(node.hash == hashValue && ((k = node.key)==key||key.equals(k))){
Object oldValue = node.value;
node.value = value;
return oldValue;
}
}
// 如果i位置没有数据,或i位置有数据,但key是新的key,新增节点
addEntry(key,value,hashValue,i);
return null;
}
@Override
public Object get(Object key) {
// 根据对象的hashcode计算hash值
int hashValue = hash(key);
// 根据hash值和链表长度,获取插入位置的索引
int i = indexFor(hashValue,table.length);
for(Node node = table[i];node != null;node = node.next){
if(node.key.equals(key) && hashValue == node.hash){
return node.value;
}
}
return null;
}
// 向Entry添加元素
// hashvalue --- hash值
// i --- 索引位置
public void addEntry(Object key,Object value,int hashValue,int i){
// 如果超过了数组约定长度,就扩容
if(++size >= table.length * DEFAULT_LOAD_FACTOR){
Node[] newTable = new Node[table.length << 1];
// 复制数组
//System.arraycopy(table,0,newTable,0,table.length);
transfer(table,newTable);
table = newTable;
}
// 得到i处的数据
Node eNode = table[i];
// 新增节点,将该节点的next指向前一个节点
table[i] = new Node(hashValue,key,value,eNode);
}
// 引用JDK1.7的复制代码
public void transfer(Node[] src,Node[] newTable) { //src引用了旧的Entry数组
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组
Node e = src[j]; //取得旧Entry数组的每个元素
if (e != null) {
src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)
do {
Node next = e.next;
int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置
e.next = newTable[i]; //标记[1]
newTable[i] = e; //将元素放在数组上
e = next; //访问下一个Entry链上的元素
} while (e != null);
}
}
}
// 获取插入的位置(取模运算 有瑕疵)
public int indexFor(int hashValue,int length){
return hashValue % length;
}
// 获取插入的位置,根据Obeject对象的hashcode 获取hash值
public int hash(Object key){
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
static class Node implements MyMapInterface.Entry{
// hash值
int hash;
Object key;
Object value;
//指向下个节点(单链表)
Node next;
Node(int hash,Object key,Object value,Node next){
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@Override
public Object getKey() {
return key;
}
@Override
public Object getValue() {
return value;
}
}
}
C.注释还是很清楚的,在了解了一部分基础知识的情况下,可以完全看动,至少我是这样过来的,需要说明遇到的一个坑,在写代码的初期,进行扩容操作的时候,我用的是System.arraycopy方法进行复制链表,但始终会出现莫名其妙的问题,因此我引入了JDK1.7的源码方法,transfer方法,进行一定的修改后,替换掉上面的复制方法,结果就正常了
D.测试类截图:
如果有错误的地方,希望各位指出,需要互相交流的可以联系我,Q806857264
如何写一个自己的HashMap的更多相关文章
- 手写一个简单的HashMap
HashMap简介 HashMap是Java中一中非常常用的数据结构,也基本是面试中的"必考题".它实现了基于"K-V"形式的键值对的高效存取.JDK1.7之前 ...
- 自己动手用java写一个hashMap
入坑java很多年了,现在总结一下自己学到的东西. 1.首先我们先来聊聊什么是HashMap? 什么是hash?hash用中文的说法就叫做“散列”,通俗的讲就是把任意长度的字符串输入,经过hash计算 ...
- 学记:为spring boot写一个自动配置
spring boot遵循"约定优于配置"的原则,使用annotation对一些常规的配置项做默认配置,减少或不使用xml配置,让你的项目快速运行起来.spring boot的神奇 ...
- 一起写一个JSON解析器
[本篇博文会介绍JSON解析的原理与实现,并一步一步写出来一个简单但实用的JSON解析器,项目地址:SimpleJSON.希望通过这篇博文,能让我们以后与JSON打交道时更加得心应手.由于个人水平有限 ...
- Java Web 开发利用Struts2+Spring+mybatis写一个用户登录界面以及简单的数据交互
框架的东西太复杂也难以讲通,直接上代码: 一.首先得配置环境 和导入必要的jar包 有一些重要的如下: Filter文件夹下的SafetyFilter.java model文件夹下的 Global ...
- 手动的写一个structs
为了更好的学习框架的运行机制,这里开始学习框架之前,介绍一个简单的自定义的框架. 需求: 登录:id:aaa,pwd:888登录成功之后,跳转到,index.jsp页面并显示,欢迎你,aaa 注册,页 ...
- 自己写一个java的mvc框架吧(四)
自己写一个mvc框架吧(四) 写一个请求的入口,以及初始化框架 上一章写了获取方法的入参,并根据入参的参数类型进行数据转换.这时候,我们已经具备了通过反射调用方法的一切必要条件.现在我们缺少一个htt ...
- 自己写一个java的mvc框架吧(三)
自己写一个mvc框架吧(三) 根据Method获取参数并转换参数类型 上一篇我们将url与Method的映射创建完毕,并成功的将映射关系创建起来了.这一篇我们将根据Method的入参参数名称.参数类型 ...
- 手把手教你写一个RPC
1.1 RPC 是什么 定义:RPC(Remote Procedure Call Protocol)--远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数 ...
随机推荐
- .NET Framework、.NET Core 和 .NET 5+ 的产品生命周期
本文整理记录了 .NET Framework..NET Core 和 .NET 各个版本的产品支持周期和操作系统兼容性. 早于 .NET Framework 2.0 和 .NET Core 2.1 的 ...
- XP系统无法进入界面 不断的反复重启-解决方法
XP系统无法进入界面 不断的反复重启-解决方法 XP系统无法进入界面 不断的反复重启-解决方法 一般都是非正常关机导致磁盘受到损坏.需要修复磁盘. 1.插入带PE的u盘,进入PE系统后 2.win+R ...
- WeChair项目Alpha冲刺(5/10)
团队项目进行情况 1.昨日进展 Alpha冲刺第五天 昨日进展: 前端:完成小程序登录态的定义 后端:成功部署项目到服务器并能通过域名访问项目 数据库:调整属性数据类型 2.今日安排 前端:完善 ...
- 使用本地http的yum源
使用http作为本地yum源 场景 在生产环境中,有大概好几十台linux同系统版本的操作系统,为了安装普通软件,现在的做法是向每台机器上上传一个iso镜像,然后将镜像挂在,配置本地的yum源,实现基 ...
- JavaWeb网上图书商城完整项目--day02-9.提交注册表单功能之servlet层实现
1.当用户在界面提交注册提交的时候,我们在UerServlet来实现具体的业务方法 标准demo: 1CommonUtils CommonUtils类就两个方法: lString uuid():生成长 ...
- 在树莓派上读取DHT11温湿度传感器-python代码实现及常见问题(全面简单易懂)
最近由于自己的课题需要,想要用在树莓派上使用DHT11温湿度传感器来读取空气中温湿度,遇到了几个问题,解决之后也对之前的知识进行了回顾,总结,特整理如下,希望能给也在学习树莓派的小伙伴们带来一些帮助. ...
- No configuration file found and no output filename configured via Cli option.报错
webpack手动配置webpack.config.js文件,打包时出现的报错,可以试试这种解决方案 报错如下: No configuration file found and no output f ...
- Convert to Ones
Convert to Ones 'You've got a string a 1 , a 2 ,-, a n a1,a2,-,an , consisting of zeros and ones. Le ...
- 二.5vue服务器展示
1.展示服务器列表前端页面 (1)写视图模版views/resources/servers.vue <template> <div class="resources-ser ...
- Golang协程池(workpool)实现
背景 因与工作相关,所以本文中的数据都进行了更改,但逻辑是一样的. 笔者的服务ServerA会请求服务ServerH获取一些数据,但ServerH的接口有个N秒内只能请求M次的限制,并返回false. ...