簡單實現一個底層資料結構為陣列 + 連結串列的HashMap,不考慮連結串列長度超過8個時變為紅黑樹的情況。
1.示例圖
2.分析需求
- put資料時:
- key值hash後的索引處沒有元素,需要建立連結串列頭節點,放到該位置的陣列空間裡。
- key值hash後的索引處有元素,說明產生Hash碰撞,需要在連結串列中結尾處掛載節點,如果在遍歷連結串列的過程中,發現了同key的資料,則執行覆蓋即可,不再繼續往下遍歷去掛載新節點。
- 假設陣列使用的空間超過了總長度的75%,那麼對陣列進行擴容。先建立新陣列,把舊資料寫到新陣列中(此時需要重新根據key計算Hash,因為資料長度變化了,影響計算結果了),在用新資料替換掉原來的舊陣列。
- get資料時:
- key值hash後的索引下標處的元素為空的話,則不存在資料。
- key值hash後的索引下標處存在連結串列的話,需要遍歷連結串列,找到key相對應的value值。
3.程式碼實現
-
Node類實現
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:25 */ public class Node { String key; String value; Node next; public Node(String key, String value, Node nextNode) { this.key = key; this.value = value; this.next = nextNode; } }
-
LinkNode類實現
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:27 */ public class ListNode { // 頭節點 Node head; /** * 新增資料,掛載連結串列的節點 * @param key * @param value */ public void addNode(String key,String value){ // 如果頭節點是空,則結束 if(head == null ){return;} // 如果頭節點不為空,則往下掛載節點 Node node = new Node(key,value,null); Node temp = head; while(true){ // 遇到相同的key,覆蓋資料 if(key.equals(temp.key)){ temp.value = value; return; } if(temp.next == null){ break; } temp = temp.next; } // 迴圈結束後則掛上資料 temp.next = node; } /** * 獲取資料 * @param key * @return */ public String getNode(String key){ if(head == null ){return null;} Node temp = head; while(true){ if(key.equals(temp.key)){ return temp.value; } if(temp.next == null){ break; } temp = temp.next; } return null; } }
-
MyHashMap類實現
package com.zaevn.hashmap; /** * @author: zae * @date: 2023/1/30 * @time: 11:27 */ public class MyHashMap { // 陣列初始化:2的n次方 ListNode[] map = new ListNode[8]; // ListNode的個數 int size; // 由於擴容時是先建立一個新陣列,因此先宣告出來 ListNode[] mapNew; int sizeNew; /** * put方法 * @param key * @param value */ public void put(String key,String value){ if(size>map.length * 0.75){ System.out.println("開始進行擴容,當前size="+size+",陣列長度為:"+map.length); doExtendMap(); System.out.println("擴容結束,當前size="+size+",陣列長度為:"+map.length); } // 1.對key進行hash演算法然後取模 int index = Math.abs(key.hashCode())%map.length; ListNode listNode = map[index]; // 如果索引位置的元素為空,則新加一個元素(建立頭節點) if(listNode == null){ ListNode listNodeNew = new ListNode(); Node node = new Node(key,value,null); listNodeNew.head = node; map[index] = listNodeNew; size ++; }else{ // 如果索引位置的元素不為空,則往連結串列中掛載資料 listNode.addNode(key,value); } } public String get(String key){ // 1.對key進行hash演算法然後取模 int index = Math.abs(key.hashCode())%map.length; if(map[index] == null){ return null; }else{ return map[index].getNode(key); } } /** * 達到閾值後開始進行擴容 */ public void doExtendMap(){ sizeNew = 0; // 1.先建立一個新的陣列,長度為原來的二倍 mapNew = new ListNode[map.length * 2]; // 2.將舊資料對映到新的陣列上(因為陣列長度變化,因此hash規則變化,所有的值需要重新計算hash值) for(int i = 0;i<map.length;i++){ ListNode listNode = map[i]; if(listNode == null){ continue; } Node temp = listNode.head; while (true){ doPutData(mapNew,temp.key,temp.value); if(temp.next == null){ break; } temp = temp.next; } } // 3.將新的陣列替換舊的陣列 map = mapNew; this.size = sizeNew; } private void doPutData(ListNode[] mapParam,String key,String value){ int index = Math.abs(key.hashCode())%mapParam.length; ListNode listNode = mapParam[index]; if(listNode == null){ ListNode listNodeNew = new ListNode(); Node node = new Node(key,value,null); listNodeNew.head = node; mapParam[index] = listNodeNew; sizeNew ++; }else{ listNode.addNode(key,value); } } public static void main(String[] args) { // 1、一般校驗 MyHashMap hashMap0=new MyHashMap(); hashMap0.put("key1","value1"); System.out.println("一般校驗:"+hashMap0.get("key1")); System.out.println("--------------------------------------------"); // 2、同key覆蓋校驗 MyHashMap hashMap1=new MyHashMap(); hashMap1.put("key2","value00"); hashMap1.put("key2","value01"); System.out.println("同key覆蓋校驗:"+hashMap1.get("key2")); System.out.println("--------------------------------------------"); // 3、雜湊碰撞校驗(k1和k9的經過雜湊計算後得到的索引都是6) MyHashMap hashMap2=new MyHashMap(); hashMap2.put("k1","value_k1"); hashMap2.put("k9","value_k9"); System.out.println("雜湊碰撞校驗:k1:"+hashMap2.get("k1")+" k9:"+hashMap2.get("k9")); System.out.println("--------------------------------------------"); // 4、擴容校驗 MyHashMap hashMap3=new MyHashMap(); hashMap3.put("m3","cccccc"); hashMap3.put("c1","kkkkkk"); hashMap3.put("c2","mmmmmmm"); hashMap3.put("b1","bbbbbbb"); hashMap3.put("m1","cccccc"); hashMap3.put("c3","kkkkkk"); hashMap3.put("c4","mmmmmmm"); hashMap3.put("b2","bbbbbbb"); hashMap3.put("m2","cccccc"); hashMap3.put("c5","kkkkkk"); hashMap3.put("c6","mmmmmmm"); hashMap3.put("b3","bbbbbbb"); System.out.println("擴容後的c4:"+hashMap3.get("c4")); System.out.println("擴容後的b3:"+hashMap3.get("b3")); } }