HashMap基本了解


1、 jdk1.7之前,HashMap底层只是数组和链表

2、 jdk1.8之后,HashMap底层数据结构当链表长度超过8时,会转为红黑树

3、 HashMap利用空间换时间的思想,将键值对一个个散落在集合中

4、 hashcode值通过调用hashcode()方法得到,所以有可能存在hashcode值相同的情况,即所谓的哈希冲突

5、手撕hashmap的思路:

6、存储put():

  • Map有一个封装的内部接口Entry<K,V>,用来将key和value封装成键值对对象
  • 键值对对象根据计算的hashcode值进行存储
  • hashmap的特点
    • key不能重复
    • 当key重复时,会把原有的键值对替换成新的键值对

7、取值get():

  • 先调用hashcode方法进行计算,判断是否存在

    • 存在则在链表中进行equals方法一一比较他们的key
    • key值不重复

手撕HashMap

1、首先需要一个Map接口,其中定义我们的put()、get()、hashcode()等方法

public interface FakeMap<K,V> {

    /**
* 将键值对存入我们自己实现的FakeMap中
* @param key 传入的key
* @param value key所对应的值
*/ void put(K key,V value); /**
* 通过传入key来获取对应的值
* @param key 传入的key
* @return 返回key对应的值,没有则返回null
*/
V get(K key); /*
说明:
hashmap所使用的hashcode方法应该来自于key本身提供
1、我们在模拟hashmap时,需要保证hashcode值的范围,不能超过数组的下表
2、jdk1.8之后接口内可以写静态方法和default方法
3、如果两个对象的hashcode相同,对象不一定相同;
如果对象相同,hashcode一定相同
*/ /**
* 自己定义的hashcode方法
* @param key 需要计算hashcode值的key
* @return int类型的hashcode值,人为地将值限制在了0-1999
*/
default int hashcode(K key){
return key.toString().hashCode()%2000;
} }

2、需要一个Entry类,用来用我们传入的键值对生成键值对对象

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; @Data
@NoArgsConstructor
@AllArgsConstructor
public class Entry<K, V> {
private K key;
private V value;
}

3、需要一个Map的实现类,用来实现Map中的各个方法

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; public class FakeHashMap<K, V> implements FakeMap<K, V> { /**
* 定义一个数组,数组的下标和hashcode值对应,用来存放链表的地址
*/
LinkedList<Entry<K, V>>[] mapArr = new LinkedList[2000]; /**
* 不需要遍历数组,大大减少了代码量,直接存入hashcode的值
* 用来记录被使用的hashcode,方便后续其他方法的操作
*/
List<Integer> hashcodeList=new ArrayList<>(); /**
* 将键值对存入我们自己实现的FakeMap中
* @param key 传入的key
* @param value key所对应的值
*/
@Override
public void put(K key, V value) {
//根据key计算出key对应的hashcode值
int hashcode = hashcode(key);
//hashcode值对应的是数组的下标,应该先判断下标对应的链表是否存在,不存在就先创建
if (null == mapArr[hashcode]) {
//创建一个链表,并且将我们的key和value封装成键值对对象Entry并存入链表
Entry<K, V> entry = new Entry(key,value);
//链表的内存地址存入数组对应的下标处
mapArr[hashcode] = new LinkedList<>();
mapArr[hashcode].add(entry);
hashcodeList.add(hashcode);
} else {
//链表存在说明之前已经有键值对存入,需要我们进行判断
//需要遍历这个链表:1、如果找到key相同的,则更新链表替换 2、如果没有找到,直接新建对象存入
boolean found = false;
loop:
for (Entry<K, V> entry : mapArr[hashcode]
) {
if (entry.getKey().equals(key)) {
entry.setValue(value);
found = true;
//若找到则退出循环
break loop;
}
}
if (!found) {
mapArr[hashcode].add(new Entry<K, V>(key, value));
}
}
} /**
* 通过传入key来获取对应的值
* @param key 传入的key
* @return 返回key对应的值,没有则返回null
*/
@Override
public V get(K key) {
int hashcode = hashcode(key);
//如果发现没存过,直接返回空
if (null == mapArr[hashcode]) {
return null;
} else {
//如果遍历能查找到key,则根据key取出对应的下标的值,返回value
//如果遍历不能找到,则返回null
for (Entry<K, V> entry : mapArr[hashcode]
) {
if (entry.getKey().equals(key)) {
return entry.getValue();
}
}
}
return null;
} }

4、最后写一个测试类,测试我们自己手搓的hashmap

  • 传入三个键值对,其中前两个的key值相同,看看是否会自己更新value值
public class Test {
public static void main(String[] args) {
FakeMap<String, String> fm = new FakeHashMap<>();
fm.put("ikun","zhangsan");
fm.put("ikun","lisi");
fm.put("boy","wangwu");
System.out.println(fm.get("ikun"));
System.out.println(fm.get("boy"));
}
}

5、测试结果:可以发现lisi替换掉了同样是ikun的zhangsan

6、补充:HashMap还有很多其他的方法,我这里没有全部手撕下来,但是可以根据put和get的思路来做

  • 具体实现的话就是在接口中定义新的方法,并且在实现类中实现再去测试就完事了
/**
* 删除传入的key值所对应的键值对对象
*
* @param key 传入的key
*/
void remove(K key); /**
* 清除 HashMap 中的所有关联或者映射
*/
void clear(); /**
* 判断是否存在key值所对应的映射,返回一个布尔值
*
* @param key 传入一个key的值
* @return 判断是否存在key值所对应的映射,返回一个布尔值
*/
boolean containsKey(K key); /**
* 获取HashMap的键的集合,以Set<K>保存
*
* @return 返回key的集合
*/
Set<K> keySet(); /**
* 得到 HashMap 中各个键值对映射关系的集合
*
* @return 返回一个映射关系的集合
*/
Set<Entry<K, V>> entrySet(); /**
* 将指定所有的键值对插入到 HashMap 中
*
* @param fakeMap 包含插入到 HashMap 的映射关系
*/
void putAll(FakeMap<K, V> fakeMap); /**
* 得到 HashMap 键值对的数量
*
* @return 一个int型整数
*/
int size(); /**
* 获取HashMap中value的集合
*
* @return 返回value集合
*/
Collection<V> values();

手撕HashMap(一)的更多相关文章

  1. 手撕HashMap

    前言: 平时工作的时候,用的最多的就是ArrayList和HashMap了,今天看了遍HashMap的源码,决定自己手写一遍HashMap. 一.创建MyHashMap接口       我们首先创建一 ...

  2. 手写HashMap,快手面试官直呼内行!

    手写HashMap?这么狠,面试都卷到这种程度了? 第一次见到这个面试题,是在某个不方便透露姓名的Offer收割机大佬的文章: 这--我当时就麻了,我们都知道HashMap的数据结构是数组+链表+红黑 ...

  3. Netty实现高性能IOT服务器(Groza)之手撕MQTT协议篇上

    前言 诞生及优势 MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,现为Cirrus Link)于1999年开发,用于监测穿越沙漠的石油管道.目标 ...

  4. 手撕RPC框架

    手撕RPC 使用Netty+Zookeeper+Spring实现简易的RPC框架.阅读本文需要有一些Netty使用基础. 服务信息在网络传输,需要讲服务类进行序列化,服务端使用Spring作为容器.服 ...

  5. 手写HASHMAP

    手写HASHMAP const int MAXN=10010; const int HASH=10100;            //需要hash的数的总个数最大值 struct HASHMAP { ...

  6. NN入门,手把手教你用Numpy手撕NN(一)

    前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...

  7. NN入门,手把手教你用Numpy手撕NN(2)

    这是一篇包含较少数学推导的NN入门文章 上篇文章中简单介绍了如何手撕一个NN,但其中仍有可以改进的地方,将在这篇文章中进行完善. 误差反向传播 之前的NN计算梯度是利用数值微分法,虽容易实现,但是计算 ...

  8. 手撕公司SSO登陆原理

    Single Sign-on SSO是老生常谈的话题了,但部分同学对SSO可能掌握的也是云里雾里,一知半解.本次手撕公司的SSO登陆原理,试图以一种简单,流畅的形式为你提供 有用的SSO登陆原理. 按 ...

  9. NN入门,手把手教你用Numpy手撕NN(三)

    NN入门,手把手教你用Numpy手撕NN(3) 这是一篇包含极少数学的CNN入门文章 上篇文章中简单介绍了NN的反向传播,并利用反向传播实现了一个简单的NN,在这篇文章中将介绍一下CNN. CNN C ...

  10. 手撕代码:统计1到n二进制数中1出现的总次数

    题目描述: 互娱手撕代码题. 统计从1到n这n个数的二进制表示中1出现的次数. 思路分析: 思路一:直接的做法是从1遍历到n,对于每个数和1做与操作,之后,对于这个数不断做右移操作,不断和1做与操作, ...

随机推荐

  1. [Git]Git统计代码行数

    1 前言 今天,有这么一个需求:小组老大要求咱们[每个人]把[上个月]的[代码行数]统计一下并上报. 成,统计就统计,但那么多项目,总不能让我用手去数吧?何况,时间久了,自己也不清楚自己改了哪些地方了 ...

  2. .NET周报 【4月第2期 2023-04-08】

    国内文章 LRU缓存替换策略及C#实现 https://www.cnblogs.com/eventhorizon/p/17290125.html 这篇文章讲述了缓存替换策略,特别是LRU算法.LRU算 ...

  3. 链式描述线性表(C++实现)

    在链式描述中,线性表的元素在内存中的存储位置是随机的,每个元素都有一个明确的指针或链指向下一个元素的位置 chain类 在此使用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向链接 ...

  4. C# 从0到实战 变量的定义与使用

    变量的定义 变量本质是一种内存的占位符,使得我们可以轻松操作计算机.C#的变量声明格式是: 类型 名称 = 值: 1 //.... 2 3 int val = 0; //定义并赋值 4 5 Conso ...

  5. 原来这就是所谓的 JSR!

    相信大家在学习 Java 的过程中,或多或少都见过 JSR 这个词.本篇文章就科普下什么是 JSR. 什么是 JSR ? JSR(Java Specification Requests),是指 Jav ...

  6. mysql安装my.cnf配置

    进入my.cnf文件//乱码修改 设置编码 character_set_server = utf8mb4 //编码 collation-server = utf8mb4_general_ci //连接 ...

  7. day08-优惠券秒杀04

    功能03-优惠券秒杀04 4.功能03-优惠券秒杀 4.7Redis优化秒杀 4.7.1优化分析 现在来回顾一下优惠券秒杀业务的两个主要问题: (1)首先是对优惠券的扣减,需要防止库存超卖现象: (2 ...

  8. Pytorch数据操作

    1.Pytorch中tensor的生成与访问 可以使用arange()创建一个张量:如,torch.arange(12)创建0开始的前12个整数: 除非特殊指定,否则新的张量将存放在内存中,并采用CP ...

  9. 【故障补牢】贪吃的 Bing 爬虫,限量供应的应对措施

    相对于[故障公告],[故障补牢]分享的是园子在发生故障后采取的亡羊补牢措施. 在上次被微软 Bing 爬宕机后(详见 [故障公告]被放出的 Bing 爬虫,又被爬宕机的园子),我们采取了2个应对措施, ...

  10. SQLlabs less1-10通关笔记

    SQLlabs 通关笔记 mysql数据结构 在练习靶场前我们需要了解以下mysql数据库结构,mysql数据库5.0以上版本有一个自带的数据库叫做information_schema,该数据库下面有 ...