• 这里再补充几个手撕HashMap的方法

1、remove()

  1. remove 方法参数值应该是键值对的键的值,当传入键值对的键的时候,remove 方法会删除对应的键值对
  2. 需要利用我们自己先前创建的 hashcodeList 来实现,hashcodeList 存入了所有被使用的 hashcode 值,方便后续的操作
  3. 在 put() 中,当添加新的键值对时,就会调用hashcodeList.add(hashcode);来存入添加的 hashcode 值
  4. hashcodeList:
    /**
* 不需要遍历数组,大大减少了代码量,直接存入hashcode的值
* 用来记录被使用的hashcode,方便后续其他方法的操作
*/
List<Integer> hashcodeList = new ArrayList<>();
  1. remove() 方法的思路:

    • 根据传入的 key 的值,遍历 hashmap
    • 当 key 的值相同时,删除它,与此同时遍历 hashcodeList
    • 当 hashcodeList 中存储的哈希值与 key 通过 hashcode(key) 方法后得到的哈希值相等时,删除这个 hashcodeList 值
  2. 代码:
     /**
* 删除传入的key值所对应的键值对对象
*
* @param key 传入的key
*/
@Override
public void remove(K key) {
int hashcode = hashcode(key);
for (Entry<K, V> entry : mapArr[hashcode]
) {
//要把hashcodeList中的hashcode删除
hashcodeList.removeIf(integer -> hashcode(entry.getKey()) == integer);
//删除 mapArr
if (entry.getKey().equals(key)) {
mapArr[hashcode].remove();
}
}
}

2、clear()

  1. clear 方法调用之后,会清除 hashmap 中所有的关联或映射,即清除所有的 key、value
  2. 思路:
    • hashcodeList 中存储的是使用过的哈希值,而 mapArr 的下标是对应的哈希值,存储的是对应的value值
    • 遍历 hashcodeList,将里面的值一个个取出来并放到 mapArr 的下标,一一调用 remove 方法
    /**
* 清除 HashMap 中的所有关联或者映射
*/
@Override
public void clear() {
for (int i = 0; i < hashcodeList.size(); i++) {
for (Entry<K, V> entry : mapArr[hashcodeList.get(i)]
) {
mapArr[hashcodeList.get(i)].remove();
//同时要把hashcodeList中的hashcode清除
hashcodeList.clear();
}
}
}

3、containsKey()

  1. 传入一个 key 的值,判断是否存在这个键所对应的键值对,存在则返回 true,不存在则返回 false
  2. 思路:
    • 先生成传入 key 的对应的哈希值
    • 判断下标为这个哈希值的数组是否为空,为空则直接返回 false
    • 如果不为空,则遍历这个数组找到相同的 key 则返回 true,否则返回 false
    • 会出现数组下标越界,如果出现,则说明不存在这个下标,自然也不存在这个哈希值,所以可以用 try、catch 环绕直接返回false
    /**
* 判断是否存在key值所对应的映射,返回一个布尔值
*
* @param key 传入一个key的值
* @return 判断是否存在key值所对应的映射,返回一个布尔值
*/
@Override
public boolean containsKey(K key) {
int hashcode = hashcode(key);
try {
//如果发现没存过,直接返回false
if (null == mapArr[hashcode]) {
return false;
} else {
//如果遍历能查找到key,则返回true
//如果遍历不能找到,则返回null
for (Entry<K, V> entry : mapArr[hashcode]
) {
if (entry.getKey().equals(key)) {
return true;
}
}
}
} catch (ArrayIndexOutOfBoundsException e) {
//只要出现数组下标越界就说明没找到,直接返回false
return false;
}
return false;
}

4、keySet()

  1. 作用很简单,返回一个集合,集合包含了所有的 key 的值
  2. 注意:是 key 的值,而不是哈希值
  3. 思路:
    • 当 hashcodeList 为空时,说明没有哈希值,自然也不存在 key,所以直接返回 null
    • 否则遍历 mapArr 数组,下标为 hashcodeList 存储的哈希值,用 getKey 取出 key
    /**
* 获取HashMap的键的集合,以Set<K>保存
*
* @return 返回key的集合
*/
@Override
public Set<K> keySet() {
//若没有hashcode值,直接返回空
if (null == hashcodeList) {
return null;
} else {
Set<K> kSet = new HashSet<>();
for (int i = 0; i < hashcodeList.size(); i++) {
//遍历 mapArr
for (Entry<K, V> entry : mapArr[hashcodeList.get(i)]
) {
kSet.add(entry.getKey());
}
}
return kSet;
}
}

5、values()

  1. 与 keySet 类似,作用是返回一个集合,其中包含了所有的 value 值
  2. 思路:
    • 当 hashcodeList 为空时,说明没有哈希值,自然也不存在 key,自然也不存在 value,所以直接返回 null
    • 否则遍历 mapArr 数组,下标为 hashcodeList 存储的哈希值,用 getValue 取出 value
    /**
* 获取HashMap中value的集合
*
* @return 返回value集合
*/
@Override
public Collection<V> values() {
//如果没有hashcode值,则直接返回空
if (null == hashcodeList) {
return null;
} else {
//生成一个集合
Collection<V> vCollection = new ArrayList<>();
for (int i = 0; i < hashcodeList.size(); i++) {
//遍历 mapArr
for (Entry<K, V> entry : mapArr[hashcodeList.get(i)]
) {
vCollection.add(entry.getValue());
}
}
return vCollection;
}
}

6、entrySet()

  1. 返回一个集合,包含了所有的键值对及其映射关系
  2. 思路:
    • 当 hashcodeList 为空时,说明没有哈希值,自然也不存在 key,自然也不存在 value,所以直接返回 null
    • 否则遍历 mapArr 数组,下标为 hashcodeList 存的哈希值,直接调用 add 方法添加
    /**
* 得到 HashMap 中各个键值对映射关系的集合
*
* @return 返回一个映射关系的集合
*/
@Override
public Set<Entry<K, V>> entrySet() {
//若没有hashcode值,直接返回空
if (null == hashcodeList) {
return null;
} else {
Set<Entry<K, V>> entrySet = new HashSet<>();
for (int i = 0; i < hashcodeList.size(); i++) {
//遍历 mapArr
for (Entry<K, V> entry : mapArr[hashcodeList.get(i)]
) {
entrySet.add(entry);
}
}
return entrySet;
}
}

7、size()

  1. size 方法就是返回一个 int 值,是 hashmap 的键值对的数量
  2. 思路:很简单,遍历 hashcodeList,存在一个哈希值就说明存在一对键值对,直接加一即可
    /**
* 得到 HashMap 键值对的数量
*
* @return 一个int型整数
*/
@Override
public int size() {
int count = 0;
for (int i = 0; i < hashcodeList.size(); i++) {
count++;
}
return count;
}

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

  1. 手撕HashMap

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

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

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

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

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

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

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

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

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

  6. 编译原理--05 用C++手撕PL/0

    前言 目录 01 文法和语言.词法分析复习 02 自顶向下.自底向上的LR分析复习 03 语法制导翻译和中间代码生成复习 04 符号表.运行时存储组织和代码优化复习 05 用C++手撕PL/0 在之前 ...

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

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

  8. paip.简化字-手写参考二简字..共98个

    paip.简化字-手写参考二简字..共98个 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/a ...

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

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

  10. 手撕RPC框架

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

随机推荐

  1. [数据库]mysql/mysqldump命令帮助说明

    1 mysql [root@test ~]# mysql --help mysql Ver 14.14 Distrib 5.7.24-27, for Linux (x86_64) using 6.0 ...

  2. 单元测试、文档测试、读写文件、StringIO和BytesIO

    1.单元测试就是为了测试程序执行的正确性 2.编写单元测试时,需要编写一个单元测试类,继承unittest TestCase类 3.单元测试最常用的断言是assert,断言期望抛出指定的异常 4.运行 ...

  3. 四月二十四号java基础知识

    1.输入输出是指程序与外部设备或其他计算机进行交互的操作2.流(stream)是指计算机各部件之间的数据流动流的内容上划分:流分为字节流和字符流3.输入流:将数据从外设或外存(如键盘.鼠标.文件等)传 ...

  4. P6185 [NOI Online #1 提高组] 序列

    给定两个长度为n的序列\(A\),\(B\). 有m个可用的操作\((t_i,u_i,v_i)\). \(t\)代表操作类型. 当\(t=1\)时,表示能够将\(A_{u_i}\)和\(A_{v_i} ...

  5. Luogu P2574 XOR的艺术 P3870 [TJOI2009]开关 P2846 [USACO08NOV]光开关Light Switching SP7259 LITE - Light Switching

    四倍经验题 简单线段树qwq(那你怎么还调了好几个小时) 修改:\(ans[cur]=(r-l+1-ans[cur]);\) 点表示的区间有多长就是有多少盏灯 亮着的关掉 暗的开启 就是上述语句了. ...

  6. SpringBoot 缓存注解的使用

    最近比较忙,没时间更新了.上一篇文章我说了如何使用Redis做缓存,文末我稍微提到了SpringBoot对缓存的支持.本篇文章就针对SpringBoot说一下如何使用. 1.SpringBoot对缓存 ...

  7. day117:MoFang:宠物栏的功能实现&宠物道具的使用

    目录 1.宠物栏的功能实现 2.宠物道具的使用 1.宠物栏的功能实现 1. 宠物的显示 2. 宠物的使用 3. 宠物的饱食度 4. 宠物的开锁 1.服务端提供显示宠物的api接口 orchard/so ...

  8. v-if与v-show造成部分元素丢失的问题——v-if复用元素问题

    pre { overflow-y: auto; max-height: 400px } img { max-width: 500px; max-height: 300px } 问题描述 在写tab切换 ...

  9. 一分钟使用Gitee,把本地项目放入gitee仓库中

    一.先创建一个Gitee账号 首先需要自己去别的地方看创建一个空仓库,然后复制仓库的地址 省略... 现有本地有项目代码,远程空仓库一个,如何把本地项目代码推到远程仓库? 1.在项目根目录初始化 Gi ...

  10. 在CentOS上安装与卸载Docker Engine

    本文参考Docker官网提供的 安装手册编写 测试使用的操作系统版本为CentOS 7.9 安装Docker Engine 要在 CentOS 上开始使用 Docker 引擎,请确保 满足先决条件,然 ...