简单易懂的HashMap使用指南:从入门到精通

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在Java中,HashMap是一种重要的数据结构,也是我们经常使用的一种存储数据的容器。但是,你是否了解HashMap的具体实现?在使用HashMap时,你是否遇到过问题或者疑惑?在本文中,我们将通过源代码解析、应用场景案例、优缺点分析等方面,深入了解HashMap这个精妙的数据结构。

摘要

本文将从以下几个方面对Java中的HashMap进行分析:

  1. 源代码解析:对HashMap的源代码进行解析,了解HashMap的具体实现;
  2. 应用场景案例:通过具体场景案例,让读者了解在实际开发中如何灵活运用HashMap;
  3. 优缺点分析:对HashMap的优缺点进行分析,帮助读者更好地掌握HashMap的适用范围;
  4. 类代码方法介绍:对HashMap中各个方法的使用方法和注意事项进行详细介绍;
  5. 测试用例:提供相关测试用例,帮助读者更好地理解HashMap的应用。

HashMap

简介

  HashMap是一种常见的键值对存储容器,其内部采用散列表实现,可以快速地查找键对应的值。具体来说,HashMap内部维护了一个Entry数组,每个Entry包含了一个键值对。HashMap使用哈希算法将键值对映射到数组中的位置,从而实现快速查找。

  在Java中,HashMap继承自AbstractMap类,实现了Map接口,提供了一系列的方法用于操作键值对。

源代码解析

  为了更好地理解HashMap的实现,我们将对其源代码进行解析。

数据结构

  HashMap内部维护了一个Entry数组,每个Entry包含了一个键值对,定义如下:

static class Entry<K,V> implements Map.Entry<K,V> {final K key;V value;Entry<K,V> next;final int hash;// ...
}

  其中,key表示键,value表示值,next表示下一个节点,hash表示键的哈希值。

插入操作

HashMap中插入一个键值对的操作可以通过以下代码实现:

public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);}if (key == null)return putForNullKey(value);int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;
}

  其中,inflateTable用于初始化Entry数组,如果Entry数组未被初始化,则调用inflateTable方法进行初始化。putForNullKey用于处理键为null的情况。hash方法用于计算键的哈希值。indexFor方法用于将哈希值映射到Entry数组的位置。for循环用于查找键是否已经存在于Entry数组中。如果键已经存在,则更新值;否则,添加新的Entry。

查找操作

HashMap中查找某个键的值的操作可以通过以下代码实现:

public V get(Object key) {if (key == null)return getForNullKey();Entry<K,V> entry = getEntry(key);return null == entry ? null : entry.getValue();
}final Entry<K,V> getEntry(Object key) {int hash = (key == null) ? 0 : hash(key);for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;}return null;
}

  其中,get方法用于获取指定键的值,如果键不存在,则返回null。getEntry方法用于查找对应的Entry。

删除操作

HashMap中删除某个键值对的操作可以通过以下代码实现:

public V remove(Object key) {Entry<K,V> e = removeEntryForKey(key);return (e == null ? null : e.value);
}final Entry<K,V> removeEntryForKey(Object key) {int hash = (key == null) ? 0 : hash(key);int i = indexFor(hash, table.length);Entry<K,V> prev = table[i];Entry<K,V> e = prev;while (e != null) {Entry<K,V> next = e.next;Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {modCount++;size--;if (prev == e)table[i] = next;elseprev.next = next;e.recordRemoval(this);return e;}prev = e;e = next;}return e;
}

  其中,remove方法用于删除指定键值对,先调用removeEntryForKey方法查找对应的Entry。如果找到对应的Entry,则将其删除;否则,不做任何操作。

应用场景案例

HashMap可以用于存储键值对,适用于如下场景:

  1. 对象属性存储和查找;
  2. 缓存实现;
  3. 计数器实现。

下面以缓存实现为例,介绍HashMap的应用:

public class Cache {private Map<String, Object> cache = new HashMap<>();public void put(String key, Object value) {cache.put(key, value);}public Object get(String key) {return cache.get(key);}public void remove(String key) {cache.remove(key);}public void clear() {cache.clear();}public int size() {return cache.size();}public boolean containsKey(String key) {return cache.containsKey(key);}public boolean containsValue(Object value) {return cache.containsValue(value);}
}

  在上述代码中,我们利用了HashMap实现了一个简单的缓存,用户可以通过put、get、remove、clear等方法来操作缓存中的对象,方便地实现了对象缓存的功能。

优缺点分析

优点

  1. 快速查找:由于HashMap采用哈希算法实现,可以快速地查找指定键对应的值;
  2. 高效存储:HashMap内部采用数组实现,可以高效地存储大量键值对;
  3. 线程不安全:HashMap在多线程环境下是不安全的,需要手动加锁保证线程安全;
  4. 可适应扩容: HashMap在添加元素过程中,当元素个数达到临界值时,会自动对数组进行扩容,保证可容纳更多元素;

缺点

  1. 内存占用:HashMap内部维护了一个数组,如果数组过大则会占用大量的内存;
  2. 不可保证顺序:HashMap内部使用哈希算法存储,因此键值对的顺序不可预测。

在实际应用中,需要根据具体情况来选择使用HashMap还是其他的容器。

类代码方法介绍

构造方法

public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);
}public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);
}public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);
}

  其中,第一个构造方法创建了默认大小的HashMap,第二个构造方法指定了HashMap的初始容量,第三个构造方法同时指定了初始容量和负载因子,第四个构造方法根据给定的Map创建了一个新的HashMap对象。

在这里插入图片描述

put方法

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}

  其中,put方法用于向HashMap中添加新的键值对,putVal方法是实际的插入操作。如果当前HashMap中的数组为空,则进行初始化;否则,根据键的哈希值计算出要插入的位置。如果该位置已经有Entry,则遍历整个链表,直到找到该键的Entry,然后更新其值;如果整个链表中不存在该键的Entry,则新建一个Entry并插入到链表头部。如果数组中该位置上的链表长度大于等于阈值,则将链表转化为红黑树。

在这里插入图片描述

get方法

public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;
}final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n; K k;if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;
}

  其中,get方法用于获取指定键对应的值,getNode方法用于查找对应的Entry。如果数组中该位置上的Entry不为空,则遍历整个链表或红黑树,直到找到该键的Entry。

在这里插入图片描述

remove方法

public V remove(Object key) {Node<K,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ?null : e.value;
}final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) {Node<K,V>[] tab; Node<K,V> p; int n, index;if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) {Node<K,V> node = null, e; K k; V v;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))node = p;else if ((e = p.next) != null) {if (p instanceof TreeNode)node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else {do {if (e.hash == hash &&((k = e.key) == key ||(key != null && key.equals(k)))) {node = e;break;}p = e;} while ((e = e.next) != null);}}if (node != null && (!matchValue || (v = node.value) == value ||(value != null && value.equals(v)))) {if (node instanceof TreeNode)((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p)tab[index] = node.next;elsep.next = node.next;++modCount;--size;afterNodeRemoval(node);return node;}}return null;
}

  其中,remove方法用于删除指定键值对,removeNode方法为实际的删除操作。如果数组中该位置上的Entry不为空,则遍历整个链表或红黑树,找到该键的Entry,并删除之。

在这里插入图片描述

其他方法

  HashMap还提供了一些其他的方法,如sizeisEmptycontainsKeycontainsValueclear等,这些方法都是用于操作HashMap中的键值对。

测试用例

  为了更好地理解HashMap的应用,我们提供了如下的测试用例,读者可以通过测试用例加深对HashMap的应用和实现的理解:

测试代码演示

package com.example.javase.collection;import java.util.HashMap;
import java.util.Map;/*** @Author ms* @Date 2023-10-22 19:47*/
public class HashMapTest {public static void main(String[] args) {Map<String, Integer> map = new HashMap<>();// test put() and get()map.put("a", 1);map.put("b", 2);map.put("c", 3);System.out.println(map.get("a")); // output: 1System.out.println(map.get("b")); // output: 2System.out.println(map.get("c")); // output: 3// test replace()map.put("a", 4);System.out.println(map.get("a")); // output: 4// test remove()map.remove("a");System.out.println(map.get("a")); // output: null// test clear()map.put("a", 1);map.put("b", 2);map.put("c", 3);map.clear();System.out.println(map.size()); // output: 0// test contains()map.put("a", 1);map.put("b", 2);map.put("c", 3);System.out.println(map.containsKey("a")); // output: trueSystem.out.println(map.containsValue(2)); // output: trueSystem.out.println(map.containsKey("d")); // output: falseSystem.out.println(map.containsValue(4)); // output: false}
}

测试结果

  根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。

在这里插入图片描述

测试代码分析

  根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。

  如上测试用例演示了如何使用 Java 中的 HashMap 类。首先,代码创建了一个空的 HashMap 对象,并使用 put() 方法添加了三个键值对。然后,代码使用 get() 方法获取这些键对应的值,并使用 replace() 方法替换掉其中一个键的值。接着,代码使用 remove() 方法删除了一个键值对,并使用 clear() 方法清空了整个 HashMap。最后,代码使用 containsKey()containsValue() 方法测试是否包含某个键或值。

小结

  HashMap是Java中一个重要的数据结构,内部维护了一个Entry数组,使用哈希算法将键值对映射到数组中的位置,实现快速查找。在实际开发中,HashMap可以用于存储对象属性、实现缓存、实现计数器等。HashMap的优点包括快速查找、高效存储、可适应扩容;缺点包括内存占用、不可保证顺序、线程不安全等。在使用时需要根据具体情况进行选择。

总结

  通过本文的阐述,我们了解了HashMap在Java中的实现和应用场景,深入了解了其数据结构、源代码实现、方法介绍和优缺点分析。同时,我们也提供了相关测试用例,希望读者能够通过实践更好地理解HashMap的应用。对于Java程序员而言,掌握HashMap的使用和内部实现是非常重要的,可以在实际开发中发挥重要作用。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

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

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

相关文章

如何查看Spring Boot 默认的数据库连接池类型

使用的Spring Boot的版本&#xff1a;2.3.4.RELEASE 先给出答案&#xff1a;com.zaxxer.hikari.HikariDataSource 怎么知道的呢&#xff1f; 新建一个Spring boot项目&#xff1a;springbootTest 配置pom.xml <dependencies><!-- SpringBoot 核心包 --><de…

【antd + vue】Failed to resolve component: a-select-option

一、问题说明 1、出现情况&#xff1a; <a-select>嵌套<a-select-option>&#xff0c;其中<a-select-option>循环&#xff0c;能正常使用&#xff0c;但是控制台警告。 2、控制台警告&#xff1a; [Vue warn]: Failed to resolve component: a-select-op…

OBS插件--图片或视频源随着声音动态缩放

音效动态调整 应用此插件的源可以根据音效声音的高低进行动态的缩放。在本例中&#xff0c;我们在当前的场景里面添加了一个小喇叭的图片源&#xff0c;在这个图片源上引用这款滤镜插件&#xff0c;然后将VLC视频源的音效用于此插件的音效。设置完成后&#xff0c;场景中的小喇…

HIVE调优-数据倾斜优化(详细版)

HIVE调优-数据倾斜优化 目录 HIVE调优-数据倾斜优化 1.排序优化 1&#xff09;order by 2&#xff09;distribute by sort by 3&#xff09;cluster by语句&#xff1a; 2.数据倾斜优化 1&#xff09;原因&#xff1a; 2&#xff09;表现&#xff1a; 3&#xff09…

css中用于设置光标颜色的属性

caret-color 是一个 CSS 属性&#xff0c;它用于定义输入光标&#xff08;caret&#xff09;的颜色。这里的“插入光标”&#xff08;insertion caret&#xff09;指的是在网页的可编辑器区域内&#xff0c;用来指示用户的输入具体会插入到哪里的那个一闪一闪的形似竖杠 | 的东…

[嵌入式系统-77]:RT-Thread-快速上手:嵌入式系统调测工具大全

目录 1. JTAG 下载调试器&#xff1a; 2. J-Link 仿真器&#xff1a; 3. ICE&#xff08;In-Circuit Emulator&#xff09;&#xff1a; 4. ROM监视器&#xff08;ROM Monitor&#xff09;&#xff1a; 5. 终端仿真工具&#xff1a; 6. 总线抓取工具&#xff1a; 7. 静态…

​​​​【收录 Hello 算法】5.2 队列

目录 5.2 队列 5.2.1 队列常用操作 5.2.2 队列实现 1. 基于链表的实现 2. 基于数组的实现 5.2.3 队列典型应用 5.2 队列 队列&#xff08;queue&#xff09;是一种遵循先入先出规则的线性数据结构。顾名思义&#xff0c;队列模拟了排队现象&#xff0c;即…

Django模型进阶

一.配置MySQL 1.安装mysql 2.mysql驱动 使用mysqlclient pip install mysqlclient 如果上面的命令安装失败&#xff0c;则尝试使用国内豆瓣源安装: pip install -i https://pypi.douban.com/simple mysglclien (Linux Ubuntu下需要先安装:apt install lib…

[C语言知识]return和exit到底有什么区别?

前言 &#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到C语言知识系列 用通俗易懂的语言让编程语言不再困难 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成本搭…

Spring @Repository 注解

Spring 的项目严重依赖注解。 Repository 注解 在Spring2.0之前的版本中&#xff0c;Repository注解可以标记在任何的类上&#xff0c;用来表明该类是用来执行与数据库相关的操作(即dao对象)&#xff0c;并支持自动处理数据库操作产生的异常 在Spring2.5版本中&#xff0c;引…

Today At Apple 2024.04.20 ipad 绘画 图片管理

文章目录 recreate 软件 绘画图片管理 官网&#xff1a; https://www.apple.com/today/Apple 亚洲第一大商店&#xff1a;Apple 静安零售店现已在上海开幕如下预约课程&#xff1a;下载 Apple Store&#xff08;不是app store&#xff09;&#xff0c;点击课程预约笔记&#xf…

GEVernova推出GEV新能源平台,引领新能源未来

近日&#xff0c;全球领先的能源设备制造和服务公司 GE Vernova 宣布推出 GEV 新能源平台&#xff0c;这是一个将金融、科技和产业深度融合的全新投资平台。GEV 新能源平台旨在为用户提供一站式可持续新能源投资解决方案&#xff0c;助力全球新能源转型和可持续发展。 新能源已…

基于MetaGPT的智能体理论与实践-Task01

Task01: MetaGPT环境配置 学习教程&#xff1a;https://github.com/datawhalechina/hugging-multi-agent 1 环境准备 1.1 安装python3.9 通过&#xff1a;python3 --version, 查看此python版本为3.10.3 1.2 下载MetaGPT 开始&#xff0c;借用清华镜像&#xff0c;拉取…

acw165. 小猫爬山-DFS剪枝与优化

题目 思路 暴搜顺序&#xff1a;从前往后依次枚举每只小猫&#xff0c;枚举当前这只小猫应该放在哪一辆车上&#xff0c;递归完n层之后&#xff0c;就可以知道所有方案中的最少车辆总数剪枝的情况&#xff1a; 优化搜索顺序&#xff1a;大部分情况下&#xff0c;应该优先搜索分…

交易复盘-20240513

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 一支股票 10%的时候可以操作, 90%的时间适合空仓等待 双成药业 (1)|[9:30]|[3566万]|0.34 中通客车 (1)|[9:43]|[7678万]|0.15 嘉华股份 (2)|[9:30]|[36…

李飞飞团队关于2024年人工智能发展报告总结 (Artificial Intelligence Index Report)

目录 1 10大核心信息2 AI研究和发展2.1 核心要点2.2 核心对比信息2.3 模型是否会用尽数据2.4 基础模型发展2.5 训练模型成本 3 技术性能3.1 核心要点3.2 重要模型发布情况3.3 AI表现情况3.4 多学科、高难度评估集 (MMMU & GPQA & ARC)3.5 Agents3.6 RLHF & RLAIF3.…

山西教育杂志山西教育杂志社山西教育编辑部2024年第4期目录

课题研究 小学语文随堂小练笔策略研究 陈立固; 3-4 儿童戏剧工作坊载体下小学语文戏剧课堂的建构 郭黎明; 5-6《山西教育》投稿&#xff1a;cn7kantougao163.com 文化生态视域下小学英语教学的传统文化融入 周慧娟; 7-8 基于“主题语境”的初中英语课堂教学研究…

公式识别软件免费的有哪些?简单好用的有三款

公式识别软件免费的有哪些&#xff1f;在数字化时代&#xff0c;公式识别软件已经成为科研、教育等领域不可或缺的工具。这些软件能够准确地将图像中的公式转化为可编辑的文本格式&#xff0c;极大地提高了工作效率。为了帮助大家轻松应对公式识别的挑战&#xff0c;今天本文就…

想要成为项目经理?先掌握这24条职场生存法则!

以下是项目经理的24条职场经验&#xff0c;这些经验涵盖了项目管理的各个方面&#xff0c;希望能对项目经理或有意成为项目经理的人有所启发。 1、明确目标&#xff1a;项目成功的关键在于明确的目标。在项目启动之初&#xff0c;确保所有利益相关者对项目目标有清晰、共同的…

绘制一个单级放大电路原理图过程,保姆级教程

新手在学习pads的使用最好最快的方法就是实际上手去画原理图&#xff0c;画PCB图&#xff0c;在这个过程中&#xff0c;就能够更快速得掌握PADS软件的使用。 本篇就是对于实际画原理图过程的一个记录&#xff0c;手把手教学&#xff0c;如果有纰漏或者有更好的一些技巧&#xf…