原諒人家寫的資料我看不懂脯丝,有興趣看看:JAVA HASHMAP的死循環(huán)
該寫的注釋已經(jīng)都寫在的代碼里面痒谴,直接拷貝就能測(cè)
package com.lw.vrv.javacore.hashmap;
import java.util.Map;
import java.util.Objects;
/**
* 自定義HashMap: 實(shí)現(xiàn)簡單put和get方法隙弛,方便理解hashcode 用取模算出厢洞,key用int方便計(jì)算模复哆,取模結(jié)果就是數(shù)組的下標(biāo)歧沪,即key=value所要存放的位置
*
*
* 創(chuàng)建類:TODO
*
* @author BruceLiu
*
* @param <K>
* @param <V>
*/
public class MyHashMap<K, V> {
static final Entry<?, ?>[] EMPTY_TABLE = {};
transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;
int capacity;
public static void main(String[] args) {
MyHashMap<Integer, Object> myHashMap = new MyHashMap<>(2);//初始化容量為2
myHashMap.put(1, "v1");
myHashMap.put(2, "v2");
myHashMap.put(3, "v3");
myHashMap.put(5, "v5");
//key%2 = 2 1,3
//array: [0] [1]
//linked: v2 v5 //后進(jìn)來的放最前面
// v3
// v1
//
System.out.println(myHashMap);
System.out.println(myHashMap.get(2));//鏈表上就一條
System.out.println(myHashMap.get(1));//鏈表上三條碳褒,分表是5,3,1
System.out.println(myHashMap.get(3));
System.out.println(myHashMap.get(5));
myHashMap.resize(4);
}
public MyHashMap(int capacity) {
this.capacity = capacity;
table = new Entry[capacity];
}
public void put(K key, V value) {
//int index = key.hashCode() % capacity;// bluckIndex
int index = (Integer)key % capacity;// 這樣簡單點(diǎn)折砸,能一眼看出來在哪個(gè)位置
/**
* 取出當(dāng)前下標(biāo)位置的Entry,作為下一個(gè)Entry
* 第一次:currentEntry = null沙峻,因?yàn)槟J(rèn)是空數(shù)組睦授,沒有數(shù)據(jù)
* 第二次:如果index下標(biāo)位置有數(shù)據(jù):取出當(dāng)前下標(biāo)的Entry對(duì)象,作為當(dāng)前key-value的下一個(gè)節(jié)點(diǎn)(新數(shù)據(jù)替換老數(shù)據(jù))
* 第三次:同第二次
* ... ...
*/
Entry<K, V> nextEntry = table[index];
table[index] = new Entry<>(index, key, value, nextEntry);
}
public Entry<K, V> get(K key) {
int index = key.hashCode() % capacity; // bluckIndex
for (Entry<K, V> e = table[index]; e != null; e = e.next) {
Object k;
if (e.hash == index && ((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
/**
* 擴(kuò)容方法:
*
* 把原來的table數(shù)據(jù)復(fù)制到新的table上摔寨,因?yàn)樗饕兞巳ゼ希谖恢靡簿妥兞耍匦沦x值
*
* @param capacity
*/
public void resize(int newCapacity){
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable; //替換成新數(shù)組
}
/**
*
* 關(guān)鍵代碼:如果是自己寫是复,你會(huì)怎么移動(dòng)old table到new table
*
* [0] [1] 代表數(shù)組的下標(biāo)位置
*
* key%2
* old table [0] [1]
* key 2 5
* 3
* 1
*
* key%4
* new table [0] [1] [2] [3]
* 1 2 3
* 5
*
*/
void transfer(Entry[] newTable) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) { //遍歷老數(shù)組
while(null != e) {
int index = (Integer)e.getKey() % newCapacity; //重新取模 新數(shù)組下標(biāo)
//參考:put方法
Entry newNextEntry = newTable[index];
newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), newNextEntry);
//錯(cuò)誤寫法:因?yàn)樵谛碌膖able中删顶,下一個(gè)節(jié)點(diǎn)不一定還是在下一個(gè)節(jié)點(diǎn),需要重新計(jì)算了淑廊,直接賦值下一個(gè)節(jié)點(diǎn)就是錯(cuò)誤的
//newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), e.getNext());
//獲取下個(gè)節(jié)點(diǎn)逗余,繼續(xù)遍歷
e = e.getNext();
//源碼 ( 邏輯是一樣的,就是代碼精簡一點(diǎn)季惩,你品录粱,你細(xì)品 )
//Entry<K,V> next = e.next; //next為臨時(shí)對(duì)象,保存老數(shù)據(jù)
//e.next = newTable[index]; //相當(dāng)于重新聲明一個(gè)新table的下一個(gè)節(jié)點(diǎn):相當(dāng)于Entry next = newTable[index];
//newTable[index] = e; //此處就是為了不重新new對(duì)象画拾,復(fù)用對(duì)象e: 相當(dāng)于newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), newNextEntry);
//e = next; //準(zhǔn)備下一次循環(huán):將next作為下一次遍歷的對(duì)象e
}
}
}
/**
* Entry對(duì)象:就是table[i]所要存放的對(duì)象啥繁,因?yàn)閷?duì)象里面包含next節(jié)點(diǎn),所以當(dāng)作單鏈?zhǔn)浇Y(jié)構(gòu)(因?yàn)闆]有定義前一個(gè)節(jié)點(diǎn)信息)
*
* 創(chuàng)建類:TODO
* @author BruceLiu
*
* @param <K>
* @param <V>
*/
static class Entry<K, V> implements Map.Entry<K, V> {
final K key;
V value;
Entry<K, V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K, V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public Entry<K, V> getNext() {
return next;
}
public void setNext(Entry<K, V> next) {
this.next = next;
}
public final String toString() {
return getKey() + "=" + getValue()+", next:"+next;
}
public final int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
}
}