【版權申明】未經博主同意,謝絕轉載!(請尊重原創,博主保留追究權)
https://blog.csdn.net/qq_36000403/article/details/91353988
出自【zzf__的博客】
底層數據結構:數組,鏈表
支持動態擴容
支持以下操作
put(K key, V value)
getIndex(K k, int length)
get(K k)
getNode(Node<K, V> node, K k)
MyMap接口
public interface MyMap<K, V> {
// 向集合中插入數據
public V put(K k, V v);
// 根據k 從Map集合中查詢元素
public V get(K k);
// 獲取集合元素個數
public int size();
// Entry的作用 === Node節點
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
}
}
具體實現MyHashMap
public class MyHashMap<K, V> implements MyMap<K, V> {
// 定義table 存放HasMap 數組元素 默認是沒有初始化容器 懶加載
Node<K, V>[] table = null;
// 實際用到table 存儲容量 大小
int size;
// HashMap默認負載因子,負載因子越小,hash衝突機率越低
float DEFAULT_LOAD_FACTOR = 0.75f;
// table默認初始大小 16
static int DEFAULT_INITIAL_CAPACITY = 16;
public V put(K key, V value) {
// 1.判斷table 數組大小是否爲空(如果爲空的情況下 ,做初始化操作)
if (table == null) {
table = new Node[DEFAULT_INITIAL_CAPACITY];
}
// 2. 判斷是否需要擴容
// 實際存儲大小=負載因子*初始容量=DEFAULT_LOAD_FACTOR0.75*DEFAULT_INITIAL_CAPACITY16=x
// 如果size>x的時候就需要開始擴容數組,擴容數組大小之前兩倍
if (size > (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY)) {
// 需要開始對table進行屬數組擴容
resize();
}
// 3.取出hash值指定下標位置對應的那個Node
int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
Node<K, V> indexNode = table[index];
Node<K, V> newNode = null;
// 4.沒有發生hash衝突(index衝突)問題
if (indexNode == null) {
newNode = new Node<K, V>(key, value, null);
size++;
// 5.發生hash衝突(index衝突)問題
} else {
Node<K, V> curNode = indexNode;
//遍歷鏈表的元素
while (curNode != null) {
// 1.key相同,修改值
//這裏即用到了equals和==是因爲 k有可能被重寫hashCode方法,並且k有可能是基本數據類型(用==比較)。
//equals和==位置不能調換,因爲如果k被重寫hashCode,假如==在前,比較的是對象的地址
if (curNode.getKey().equals(key) || curNode.getKey() == key) {
return curNode.setValue(value);
//2 key不同,添加到鏈表末尾
// 兩種情況: 1.hascode不同但取模餘數相同 2.或者hashCode 相同
} else if (curNode.next == null){
// 說明遍歷到最後一個node且該操作不是修改值。創建新的node,下一個結點爲indexNode
newNode = new Node<K, V>(key, value, indexNode);
size++;
}
//取鏈表下一個node
curNode = curNode.next;
}
}
//將新創建的node放在table[index]
table[index] = newNode;
return null;
}
// 對table進行擴容
private void resize() {
// 1.生成新的table 是之前的兩倍的大小 DEFAULT_INITIAL_CAPACITY*2
Node<K, V>[] newTable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
// 2.重新計算index索引,存放在新的table裏面
for (int i = 0; i < table.length; i++) {
//存放在之前的table的Node
Node<K, V> oldNode = table[i];
//遍歷該鏈表,將oldTable的值一個個放進新的table中
while (oldNode != null) {
//table[i] = null;// 賦值爲null---爲了垃圾回收機制能夠回收 將之前的node刪除
//1.重新計算該Node在新table的index
K oldK = oldNode.key;
int newIndex = getIndex(oldK, newTable.length);
//2.這裏記錄oldNext,因爲下面將oldNodex.next指向新table的結點
Node<K, V> oldNext = oldNode.next;
//3.oldNode每次都插入newTable[newIndex],如果newTable[newIndex]已經有值,則形成鏈表
oldNode.next = newTable[newIndex];
newTable[newIndex] = oldNode;
//4.繼續取oldNext
oldNode = oldNext;
}
}
// 3.將newtable賦值給老的table
table = newTable;
DEFAULT_INITIAL_CAPACITY = newTable.length;
newTable = null;// 賦值爲null---爲了垃圾回收機制能夠回收
}
public int getIndex(K k, int length) {
int hashCode = k.hashCode();
int index = hashCode % length;
return index;
}
public V get(K k) {
Node<K, V> node = getNode(table[getIndex(k, DEFAULT_INITIAL_CAPACITY)], k);
return node == null ? null : node.value;
}
public Node<K, V> getNode(Node<K, V> node, K k) {
while (node != null) {
if (node.getKey().equals(k)) {
return node;
}
node = node.next;
}
return null;
}
public int size() {
return size;
}
// 測試方法.打印所有的鏈表元素
void print() {
for (int i = 0; i < table.length; i++) {
Node<K, V> node = table[i];
System.out.print("下標位置[" + i + "]");
while (node != null) {
System.out.print("[ key:" + node.getKey() + ",value:" + node.getValue() + "]");
node = node.next;
}
System.out.println();
}
}
// 定義節點
class Node<K, V> implements Entry<K, V> {
// 存放Map 集合 key
private K key;
// 存放Map 集合 value
private V value;
// 下一個節點Node
private Node<K, V> next;
public Node(K key, V value, Node<K, V> next) {
super();
this.key = key;
this.value = value;
this.next = next;
}
public K getKey() {
return this.key;
}
public V getValue() {
return this.value;
}
public V setValue(V value) {
// 設置新值的返回老的 值
V oldValue = this.value;
this.value = value;
return oldValue;
}
}
}
總結:
在put時再進行容器的初始化,整個過程下來,我覺得最難的那部分就是擴容,當出現索引衝突(hash衝突)時如何處理,這個流程各位朋友自己嘗試實現一遍相信會理解很多。