Java寫哈希表的完整實例代碼
一、什么叫哈希表(HashMap)?
哈希表的實質(zhì)是一種結(jié)合數(shù)組和鏈表優(yōu)勢的復(fù)合數(shù)據(jù)結(jié)構(gòu),類似于 Java 官方提供的 HashMap 集合類,不同的是它是我們基于哈希函數(shù) + 鏈表解決沖突的思想手動實現(xiàn)的底層存儲結(jié)構(gòu)。因為其本質(zhì)是 “數(shù)組 + 鏈表” 的組合存儲邏輯,而非原生的數(shù)據(jù)類型,所以需要通過定義哈希函數(shù)、鏈表節(jié)點、核心操作方法來賦予其可操作的能力,只有被實例化為對象時,才能完成鍵值對的增刪查等操作。
哈希表以數(shù)組為底層基礎(chǔ)容器,通過哈希函數(shù)將鍵(Key)映射到數(shù)組的指定索引位置;當(dāng)多個鍵映射到同一索引時(哈希沖突),則通過鏈表將這些沖突的鍵值對串聯(lián)存儲,既保留了數(shù)組隨機訪問的高效性,又解決了數(shù)組固定長度、沖突存儲的問題。
二、定義自定義 HashMap 的方法?
(一)先定義核心組件
1. 節(jié)點類(Node)
訪問修飾符 + class + 類名
節(jié)點類是哈希表中存儲鍵值對的最小單位,需要定義存儲鍵、值的屬性,以及指向下一個節(jié)點的引用(用于鏈表串聯(lián))。
(1)節(jié)點類的屬性:
理解:節(jié)點的屬性可以看成存儲單個鍵值對的核心特征(比如鍵 key、值 value,以及鏈表中下一個節(jié)點的引用 next)。定義格式:訪問修飾符 + 數(shù)據(jù)類型 + 屬性名
(2)節(jié)點類的構(gòu)造方法:
理解:用于初始化節(jié)點的鍵和值,給 next 引用賦默認(rèn)值。
定義格式:訪問修飾符 + 類名(參數(shù)類型 + 參數(shù)名,…){屬性賦值…}
2. 鏈表類(LinkList)
訪問修飾符 + class + 類名
鏈表類用于解決哈希沖突,存儲數(shù)組同一索引下的所有沖突鍵值對,需要定義鏈表的頭節(jié)點屬性,以及添加、查詢鍵值對的方法。
(1)鏈表類的屬性:
理解:鏈表的屬性是其核心特征(比如頭節(jié)點 head,作為鏈表遍歷的起點)。定義格式:訪問修飾符 + 數(shù)據(jù)類型 + 屬性名
(2)鏈表類的方法:
理解:鏈表的方法是其核心行為(比如添加 / 覆蓋鍵值對、根據(jù)鍵查詢值)。
定義格式:訪問修飾符 + 返回值類型 + 方法名(參數(shù)類型 + 參數(shù)名,…){方法體…}
3. 哈希表主類(MyHashMap)
訪問修飾符 + class + 類名
哈希表主類是對外提供操作接口的核心類,需要定義存儲鏈表的數(shù)組、數(shù)組默認(rèn)長度等屬性,以及構(gòu)造方法、哈希函數(shù)、put/get 核心方法。
(1)哈希表的屬性:
理解:哈希表的屬性是其核心特征(比如存儲鏈表的數(shù)組 linkLists、數(shù)組默認(rèn)長度 len)。
定義格式:訪問修飾符 + 數(shù)據(jù)類型 + 屬性名
(2)哈希表的方法:
理解:哈希表的方法是其核心行為(比如哈希函數(shù) hash ()、存儲鍵值對 put ()、查詢值 get ())。
定義格式:訪問修飾符 + 返回值類型 + 方法名(參數(shù)類型 + 參數(shù)名,…){方法體…}
class Node {
Object key;
Object value;
Node next;
// 構(gòu)造方法:初始化鍵值對,next默認(rèn)null
public Node(Object key, Object value) {
this.key = key;
this.value = value;
this.next = null;
}
}class LinkList {
// 鏈表頭節(jié)點
private Node head;
// 添加/覆蓋鍵值對:存在相同key則覆蓋value,不存在則新增節(jié)點
public void add(Object key, Object value) {
// 頭節(jié)點為空,直接創(chuàng)建新節(jié)點作為頭節(jié)點
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;
}
// 到鏈表尾部,退出循環(huán)
if (current.next == null) {
break;
}
current = current.next;
}
// 無相同key,在鏈表尾部新增節(jié)點
current.next = new Node(key, value);
}
// 根據(jù)key獲取對應(yīng)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;
}
// 未找到對應(yīng)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 {
//定義保存鏈表的數(shù)組
public LinkList[] linkLists;
public static int len = 16;
//自定義長度
public MyHashMap(int len) {
linkLists = new LinkList[len];
//初始化數(shù)組,每個位置都創(chuàng)建空鏈表
for(int i=0;i<len;i++){
linkLists[i] = new LinkList();
}
}
//默認(rèn)長度
public MyHashMap() {
this(len);
}
//put數(shù)據(jù):存儲鍵值對,鍵重復(fù)則覆蓋值
public void put(Object key, Object value) {
//根據(jù)當(dāng)前key,利用哈希函數(shù)計算位置
int index = hash(key);
//取出對應(yīng)鏈表,保存鍵值對(處理重復(fù)鍵覆蓋)
linkLists[index].add(key, value);
}
//get 取出數(shù)據(jù):根據(jù)key獲取對應(yīng)value,無則返回null
public Object get(Object key){
int index = hash(key);
return linkLists[index].get(key);
}
//哈希函數(shù)(散列函數(shù)):計算key在數(shù)組中的索引,處理負(fù)數(shù)哈希值
public int hash(Object key) {
if (key == null) {
return 0; // null鍵固定放在索引0位置
}
int hashCode = key.hashCode();
// 處理負(fù)數(shù)哈希值,保證索引非負(fù)
return (hashCode & 0x7FFFFFFF) % linkLists.length;
}
public static void main(String[] args) {
MyHashMap hm = new MyHashMap();
hm.put("a",10);
hm.put("a",20); // 重復(fù)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. 哈希函數(shù)的作用
(1)核心功能:將任意類型的鍵(Key)映射為數(shù)組的索引,公式為 (key.hashCode() & 0x7FFFFFFF) % 數(shù)組長度;(2)關(guān)鍵處理:
null 鍵特殊處理:固定映射到索引 0 位置,符合 Java 官方 HashMap 的設(shè)計;
負(fù)數(shù)哈希值處理:通過 & 0x7FFFFFFF 將哈希值轉(zhuǎn)為正數(shù),避免索引為負(fù)數(shù)的異常。
2. put 方法核心流程
(1)調(diào)用哈希函數(shù)計算鍵對應(yīng)的數(shù)組索引;(2)取出該索引位置的鏈表,調(diào)用鏈表的 add 方法;(3)鏈表 add 方法邏輯:
若鏈表為空,直接創(chuàng)建新節(jié)點作為頭節(jié)點;
若鏈表非空,遍歷查找是否有相同 key,有則覆蓋 value,無則在鏈表尾部新增節(jié)點。
3. get 方法核心流程
(1)調(diào)用哈希函數(shù)計算鍵對應(yīng)的數(shù)組索引;(2)取出該索引位置的鏈表,調(diào)用鏈表的 get 方法;(3)鏈表 get 方法邏輯:遍歷鏈表匹配 key,匹配成功則返回對應(yīng) value,無匹配則返回 null。
四、補充說明
- 哈希沖突解決:本文采用鏈地址法(鏈表)解決哈希沖突,這是 Java 官方 HashMap 的核心實現(xiàn)方式(JDK1.8 后,當(dāng)鏈表長度超過閾值會轉(zhuǎn)為紅黑樹,本文簡化為純鏈表);
- 邊界處理:兼容 null 鍵和 null 值的存儲、查詢,處理了哈希值為負(fù)數(shù)的異常場景,保證索引合法性;
- 核心特性:實現(xiàn)了 HashMap 最核心的 “鍵唯一、值可重復(fù)、鍵重復(fù)覆蓋值” 的特性,與官方 HashMap 行為一致。
到此這篇關(guān)于Java寫哈希表的文章就介紹到這了,更多相關(guān)Java寫哈希表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot配置使用H2數(shù)據(jù)庫的簡單教程
H2是一個Java編寫的關(guān)系型數(shù)據(jù)庫,它可以被嵌入Java應(yīng)用程序中使用,或者作為一個單獨的數(shù)據(jù)庫服務(wù)器運行。本文將介紹SpringBoot如何配置使用H2數(shù)據(jù)庫2021-05-05
Java 實戰(zhàn)項目之精品養(yǎng)老院管理系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+Springboot+Maven+mybatis+Vue+Mysql實現(xiàn)一個精品養(yǎng)老院管理系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11
Jdk1.8 HashMap實現(xiàn)原理詳細(xì)介紹
這篇文章主要介紹了Jdk1.8 HashMap實現(xiàn)原理詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12
Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解
這篇文章主要介紹了Java中的BlockingQueue阻塞隊列原理以及實現(xiàn)詳解,在最常見的使用到這個阻塞隊列的地方,就是我們耳熟能詳?shù)木€程池里面了,作為我們線程池的一大最大參與者,也是AQS的一個具體實現(xiàn),需要的朋友可以參考下2023-12-12
java對象和json的來回轉(zhuǎn)換知識點總結(jié)
在本篇文章里小編給大家分享了一篇關(guān)于java對象和json的來回轉(zhuǎn)換知識點總結(jié)內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2021-01-01

