Java写哈希表的完整实例代码
作者:洛洛书
一、什么叫哈希表(HashMap)?
哈希表的实质是一种结合数组和链表优势的复合数据结构,类似于 Java 官方提供的 HashMap 集合类,不同的是它是我们基于哈希函数 + 链表解决冲突的思想手动实现的底层存储结构。因为其本质是 “数组 + 链表” 的组合存储逻辑,而非原生的数据类型,所以需要通过定义哈希函数、链表节点、核心操作方法来赋予其可操作的能力,只有被实例化为对象时,才能完成键值对的增删查等操作。
哈希表以数组为底层基础容器,通过哈希函数将键(Key)映射到数组的指定索引位置;当多个键映射到同一索引时(哈希冲突),则通过链表将这些冲突的键值对串联存储,既保留了数组随机访问的高效性,又解决了数组固定长度、冲突存储的问题。
二、定义自定义 HashMap 的方法?
(一)先定义核心组件
1. 节点类(Node)
访问修饰符 + class + 类名
节点类是哈希表中存储键值对的最小单位,需要定义存储键、值的属性,以及指向下一个节点的引用(用于链表串联)。
(1)节点类的属性:
理解:节点的属性可以看成存储单个键值对的核心特征(比如键 key、值 value,以及链表中下一个节点的引用 next)。定义格式:访问修饰符 + 数据类型 + 属性名
(2)节点类的构造方法:
理解:用于初始化节点的键和值,给 next 引用赋默认值。
定义格式:访问修饰符 + 类名(参数类型 + 参数名,…){属性赋值…}
2. 链表类(LinkList)
访问修饰符 + class + 类名
链表类用于解决哈希冲突,存储数组同一索引下的所有冲突键值对,需要定义链表的头节点属性,以及添加、查询键值对的方法。
(1)链表类的属性:
理解:链表的属性是其核心特征(比如头节点 head,作为链表遍历的起点)。定义格式:访问修饰符 + 数据类型 + 属性名
(2)链表类的方法:
理解:链表的方法是其核心行为(比如添加 / 覆盖键值对、根据键查询值)。
定义格式:访问修饰符 + 返回值类型 + 方法名(参数类型 + 参数名,…){方法体…}
3. 哈希表主类(MyHashMap)
访问修饰符 + class + 类名
哈希表主类是对外提供操作接口的核心类,需要定义存储链表的数组、数组默认长度等属性,以及构造方法、哈希函数、put/get 核心方法。
(1)哈希表的属性:
理解:哈希表的属性是其核心特征(比如存储链表的数组 linkLists、数组默认长度 len)。
定义格式:访问修饰符 + 数据类型 + 属性名
(2)哈希表的方法:
理解:哈希表的方法是其核心行为(比如哈希函数 hash ()、存储键值对 put ()、查询值 get ())。
定义格式:访问修饰符 + 返回值类型 + 方法名(参数类型 + 参数名,…){方法体…}
class Node {
Object key;
Object value;
Node next;
// 构造方法:初始化键值对,next默认null
public Node(Object key, Object value) {
this.key = key;
this.value = value;
this.next = null;
}
}class LinkList {
// 链表头节点
private Node head;
// 添加/覆盖键值对:存在相同key则覆盖value,不存在则新增节点
public void add(Object key, Object value) {
// 头节点为空,直接创建新节点作为头节点
if (head == null) {
head = new Node(key, value);
return;
}
// 遍历链表,查找是否存在相同key
Node current = head;
while (current != null) {
// key相等(处理null key),覆盖value
if (equals(key, current.key)) {
current.value = value;
return;
}
// 到链表尾部,退出循环
if (current.next == null) {
break;
}
current = current.next;
}
// 无相同key,在链表尾部新增节点
current.next = new Node(key, value);
}
// 根据key获取对应value,无则返回null
public Object get(Object key) {
Node current = head;
while (current != null) {
// 匹配key(处理null key)
if (equals(key, current.key)) {
return current.value;
}
current = current.next;
}
// 未找到对应key
return null;
}
// 辅助方法:判断两个key是否相等(处理null值)
private boolean equals(Object k1, Object k2) {
if (k1 == null && k2 == null) {
return true;
}
if (k1 == null || k2 == null) {
return false;
}
return k1.equals(k2);
}
}public class MyHashMap {
//定义保存链表的数组
public LinkList[] linkLists;
public static int len = 16;
//自定义长度
public MyHashMap(int len) {
linkLists = new LinkList[len];
//初始化数组,每个位置都创建空链表
for(int i=0;i<len;i++){
linkLists[i] = new LinkList();
}
}
//默认长度
public MyHashMap() {
this(len);
}
//put数据:存储键值对,键重复则覆盖值
public void put(Object key, Object value) {
//根据当前key,利用哈希函数计算位置
int index = hash(key);
//取出对应链表,保存键值对(处理重复键覆盖)
linkLists[index].add(key, value);
}
//get 取出数据:根据key获取对应value,无则返回null
public Object get(Object key){
int index = hash(key);
return linkLists[index].get(key);
}
//哈希函数(散列函数):计算key在数组中的索引,处理负数哈希值
public int hash(Object key) {
if (key == null) {
return 0; // null键固定放在索引0位置
}
int hashCode = key.hashCode();
// 处理负数哈希值,保证索引非负
return (hashCode & 0x7FFFFFFF) % linkLists.length;
}
public static void main(String[] args) {
MyHashMap hm = new MyHashMap();
hm.put("a",10);
hm.put("a",20); // 重复key,覆盖值
hm.put("c",null);
hm.put(null, 99);
System.out.println(hm.get("a"));
System.out.println(hm.get("c"));
System.out.println(hm.get(null));
System.out.println(hm.get("d"));
}
}三、自定义 HashMap 核心逻辑解析
1. 哈希函数的作用
(1)核心功能:将任意类型的键(Key)映射为数组的索引,公式为 (key.hashCode() & 0x7FFFFFFF) % 数组长度;(2)关键处理:
null 键特殊处理:固定映射到索引 0 位置,符合 Java 官方 HashMap 的设计;
负数哈希值处理:通过 & 0x7FFFFFFF 将哈希值转为正数,避免索引为负数的异常。
2. put 方法核心流程
(1)调用哈希函数计算键对应的数组索引;(2)取出该索引位置的链表,调用链表的 add 方法;(3)链表 add 方法逻辑:
若链表为空,直接创建新节点作为头节点;
若链表非空,遍历查找是否有相同 key,有则覆盖 value,无则在链表尾部新增节点。
3. get 方法核心流程
(1)调用哈希函数计算键对应的数组索引;(2)取出该索引位置的链表,调用链表的 get 方法;(3)链表 get 方法逻辑:遍历链表匹配 key,匹配成功则返回对应 value,无匹配则返回 null。
四、补充说明
- 哈希冲突解决:本文采用链地址法(链表)解决哈希冲突,这是 Java 官方 HashMap 的核心实现方式(JDK1.8 后,当链表长度超过阈值会转为红黑树,本文简化为纯链表);
- 边界处理:兼容 null 键和 null 值的存储、查询,处理了哈希值为负数的异常场景,保证索引合法性;
- 核心特性:实现了 HashMap 最核心的 “键唯一、值可重复、键重复覆盖值” 的特性,与官方 HashMap 行为一致。
到此这篇关于Java写哈希表的文章就介绍到这了,更多相关Java写哈希表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
