手撕HashMap主要是為了能更好的理解HashMap的數據結構原理。只實現了 put、get、remove。
JDK 實現的實在太復雜。這個實現是實現最簡單的版本。后續如果有時間會逐一補上 自動擴容,數組+紅黑樹的實現。
前提條件
- 數組+鏈表有基本了解
實現邏輯
package com.company;
public class MaLinHashMap {
private int arrayLength;
private Node[] bucketArray;
public MaLinHashMap() {
this.arrayLength = 16;
this.bucketArray = new Node[arrayLength];
}
public MaLinHashMap(int arrayLength) {
this.arrayLength = arrayLength;
this.bucketArray = new Node[arrayLength];
}
private class Node {
private Object key;
private Object value;
private Node next;
public Node(Object key, Object value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
public void put(Object key, Object value) {
int index = getBucketArrayIndex(key);
Node headNode = bucketArray[index];
Node newNode = new Node(key, value, null);
if (headNode == null) {
bucketArray[index] = newNode;
} else {
while (true) {
if (headNode.next == null) {
headNode.next = newNode;
break;
}
headNode = headNode.next;
}
}
}
public void remove(Object key) {
int index = getBucketArrayIndex(key);
Node head = bucketArray[index];
Node prev = null;
while (head != null) {
if (head.key.equals(key))
break;
prev = head;
head = head.next;
}
if (head == null) {
return;
}
if (prev != null) {
prev.next = head.next;
} else {
bucketArray[index] = head.next;
}
}
public Object get(Object key) {
int index = getBucketArrayIndex(key);
Node headNode = bucketArray[index];
while (headNode != null) {
if (headNode.key.equals(key)) return headNode.value;
headNode = headNode.next;
}
return null;
}
/**
* 獲取數組下標
*
* @param key 鍵
* @return 下標
*/
private int getBucketArrayIndex(Object key) {
// 這里不采用 & 與運算符,直接使用hash%arrayLength可能出現負數非常尷尬。
// return key.hashCode() % arrayLength;
// TODO & 使用后可以確保得到一個 0~15 整數,但是位運算符實戰看不懂。這個還是抄襲的HashMap獲取index
// if ((p = tab[i = (n - 1) & hash]) == null)
int hash = (hash = key.hashCode()) ^ (hash >>> 16);
return arrayLength - 1 & hash;
}
}
測試邏輯
package com.company;
public class MaLinHashMapTest {
public static void main(String[] args) {
int count = 100000;
test(32768, count);
test(16384, count);
test(8192, count);
test(4096, count);
test(2048, count);
test(1024, count);
test(512, count);
test(256, count);
test(128, count);
test(64, count);
test(32, count);
test(16, count);
}
public static void test(int arrayLength, int count) {
long start = System.currentTimeMillis();
MaLinHashMap map = new MaLinHashMap(arrayLength);
for (int i = 0; i < count; i++) {
map.put("testKey" + i, i);
}
long timeConsuming = System.currentTimeMillis()-start;
String output = String.format("數組長度%s, 數據量:%s, 耗時(毫秒): %s", arrayLength, count, timeConsuming);
System.out.println(output);
}
}
測試結果
"C:\Program Files\Java\jdk1.8.0_211\bin\java.exe" -Xms2048m -Xmx2048m "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.1.1\lib\idea_rt.jar=56358:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.1.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_211\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_211\jre\lib\rt.jar;F:\workspace\linked_learn\out\production\linked_learn" com.company.MaLinHashMapTest
數組長度32768, 數據量:10000, 耗時(毫秒): 4
數組長度16384, 數據量:10000, 耗時(毫秒): 2
數組長度8192, 數據量:10000, 耗時(毫秒): 1
數組長度4096, 數據量:10000, 耗時(毫秒): 2
數組長度2048, 數據量:10000, 耗時(毫秒): 2
數組長度1024, 數據量:10000, 耗時(毫秒): 1
數組長度512, 數據量:10000, 耗時(毫秒): 2
數組長度256, 數據量:10000, 耗時(毫秒): 3
數組長度128, 數據量:10000, 耗時(毫秒): 6
數組長度64, 數據量:10000, 耗時(毫秒): 10
數組長度32, 數據量:10000, 耗時(毫秒): 18
數組長度16, 數據量:10000, 耗時(毫秒): 25
Process finished with exit code 0