關於hashmap的問題,經常被問到。雖然看了一下源碼和相關博客,記憶還是不太深刻。發現只有手寫之後,對其中的運行原理纔有更深刻的理解。這裏記錄第一版,實現了存儲、查詢、刪除、擴容操作,是線程不安全的,且沒有實現迭代器功能。鏈表裏面沒有用到紅黑樹。一步步迭代,先記錄自己的第一步。
package www.dustin.li.collections;
/**
* 手寫集合
*
* @param <K>
* @param <V>
*/
public class MyHashMap<K, V> {
private int CAPACITY = 16;
private int SIZE = 0;
private int USED_SIZE = 0;
private float threshold = 0.75F;
Node<K, V>[] nodes = new Node[CAPACITY];
/**
* 存儲鍵值對
*
* @param key
* @param value
* @return
*/
public boolean put(K key, V value) {
int index = this.getIndex(key);
Node<K, V> node = null;
try {
node = nodes[index];
} catch (Exception e) {
System.out.println("hashCode=" + key.hashCode());
System.out.println("index=" + index);
}
if (node == null) {
System.out.println("插入,index=" + index + ",數組首位");
node = new Node<>();
node.setKey(key);
node.setValue(value);
nodes[index] = node;
SIZE++;
USED_SIZE++;
if (checkThreshold()) {
this.resize();
}
} else {
while (node.nextNode != null) {
if (node.getValue().equals(value)) {
System.out.println("插入,index=" + index + ",相同的鍵值對,直接返回");
return true;
}
node = node.nextNode;
}
Node<K, V> nextNode = new Node<>();
nextNode.setKey(key);
nextNode.setValue(value);
node.nextNode = nextNode;
SIZE++;
}
return true;
}
/**
* 根據key獲取元素
*
* @param key
* @return
*/
public V get(K key) {
int index = this.getIndex(key);
Node<K, V> headNode = nodes[index];
if (headNode == null) {
return null;
}
if (headNode.getKey().equals(key)) {
return headNode.getValue();
}
for (; ; ) {
Node<K, V> nextNode = headNode.nextNode;
if (nextNode == null) {
System.out.println("根據key沒有找到對應的value");
return null;
}
if (nextNode.getKey().equals(key)) {
System.out.println("根據key找到對應的value");
return nextNode.getValue();
}
headNode = nextNode;
if (headNode == null) {
return null;
}
}
}
/**
* 移除元素
*
* @param key
* @return
*/
public V remove(K key) {
int index = this.getIndex(key);
Node<K, V> headNode = nodes[index];
if (headNode == null) {
System.out.println("根據key刪除鍵值對失敗,沒有找到對應數據,index=" + index + ",key=" + key);
return null;
}
if (headNode.getKey().equals(key)) {
SIZE--;
USED_SIZE--;
nodes[index] = headNode.nextNode;
System.out.println("刪除鍵值對成功,直接刪除數組上的元素,index=" + index + ",key=" + key);
return headNode.getValue();
}
for (; ; ) {
Node<K, V> nextNode = headNode.nextNode;
if (nextNode == null) {
System.out.println("沒有找到對應的數據,index=" + index + ",key=" + key);
return null;
}
if (key.equals(nextNode.getKey())) {
SIZE--;
headNode.nextNode = nextNode.nextNode;
System.out.println("刪除鍵值對成功,刪除鏈表上的元素,index=" + index + ",key=" + key);
return nextNode.getValue();
}
headNode = headNode.nextNode;
}
}
/**
* 檢查是否超過閥值
*
* @return
*/
private boolean checkThreshold() {
return USED_SIZE / Float.valueOf(CAPACITY) > threshold;
}
/**
* 當元素超過閥值,重新擴容
*/
private void resize() {
int oldCapacity = CAPACITY;
CAPACITY = 2 * CAPACITY;
Node<K, V>[] oldNodes = nodes;
nodes = new Node[CAPACITY];
SIZE = 0;
for (int i = 0; i < oldCapacity; i++) {
Node<K, V> oldHeadNode = oldNodes[i];
if (oldHeadNode == null) {
continue;
}
System.out.println("重新擴容,遷移數組上的數據");
this.put(oldHeadNode.getKey(), oldHeadNode.getValue());
System.out.println("重新擴容,開始遷移鏈表上的數據");
for (; ; ) {
Node<K, V> oldNextNode = oldHeadNode.nextNode;
if (oldNextNode == null) {
break;
}
if (oldNextNode != null) {
this.put(oldNextNode.getKey(), oldNextNode.getValue());
}
oldHeadNode = oldNextNode;
}
}
}
private static class Node<K, V> {
private K key;
private V value;
private Node<K, V> nextNode;
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
/**
* 計算元素所在的數組下標
*
* @param key
* @return
*/
private int getIndex(K key) {
int hashCode = key.hashCode();
if (hashCode < 0) {
hashCode = -hashCode;
}
return hashCode % CAPACITY;
}
public int getSIZE() {
return SIZE;
}
}
測試用例:
package www.dustin.li.collections;
public class MyHashMapTest {
public static void main(String[] args) {
MyHashMap<String, String> myHashMap = new MyHashMap();
for (int i = 0; i < 100; i++) {
String key = "key_" + i;
String value = "value_" + i;
myHashMap.put(key, value);
}
for (int i = 0; i < 100; i++) {
String key = "key_" + i;
System.out.println("查詢測試,i=" + i + " value=" + myHashMap.get(key));
}
for (int i = 0; i < 100; i++) {
String key = "key_" + i;
System.out.println("刪除前,size="+myHashMap.getSIZE());
System.out.println("刪除測試,i=" + i + " value=" + myHashMap.remove(key));
System.out.println("刪除後,size="+myHashMap.getSIZE());
}
}
}
總結:
功能雖然簡單,並沒有一次性寫對。經過幾次測試才實現正常的邏輯。對自己邏輯思維有一定的鍛鍊。