Practice 1 HashMap Design
// Some code
// Some code
public static class MyHashMap<String, Integer> {
public static class Entry<String, Integer>{ //本质上是个ListNode
private final String key;
private Entry<String, Integer> next;
public Entry(String key, int value) {
this.key = key
this.value = value
}
}
private static final int DEFAULT_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private Entry<String, Integer>[] buckets; // array of ListList ==> array of listnode(entry)
private int capacity;
private int size; // size(你现在拉了多少) vs capacity(最多拉多少)
private float loadFactor;
//用户提供capacity和loadFactor版本
public MyHashMap(int capacity, float loadFactor) {
this.capacity = capacity;
this.loadFactor = loadFactor;
this.size = 0;
this.buckets = new Entry[capacity];
}
//用户不提供capacity和loadFactor版本
public MyHashMap(int capacity, float loadFactor) {
this.capacity = DEFAULT_CAPACITY;
this.loadFactor = DEFAULT_LOAD_FACTOR;
this.size = 0;
this.buckets = new Entry[capacity];
}
/* OOD:
public MyHashMap() {
this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR);
}
*/
public int size(){
return this.size;
}
public boolean isEmpty(){
return this.size == 0;
}
private boolean equalsKey(String key1, String key2) {
return key1.equals(key2);
}
//给我一个key, 我就还给你一个hashCode,保证这个hashCode不会超界,也不会是负数
private int hash(String key) {
if (key == null) {
return 0;
}
return key.hashCode() & 0X7FFFFFFFF
}
// 16, 1,2,3,4,5,6,7,8,9,A,B,C,D,E,F
// OX7FFFFFFFF => 0111 1111 1111 1111 1111 1111 1111 1111.
// 1 & anynumber = any number, 0 & anynumber = 0
// 既然说我只要有个key就有hashcode了,我怎么知道这个key应该在我的map的array里的哪个index bucket
// 你给我一个key,我能告诉你这个key在哪个bucket里
private int getIndex(String key) {
int hashCode = hash(key)
return hashCode % this.capacity;
}
// rehashing: 扩容 这个方法能告诉我们现在的hashmap的状况,需不需要rehash
// 如果当前的map里的entry的总数/capacity =》 你设置的load factor了,那么我们就应该,否不不应该
private boolean needRehashing() {
float ratio = (size + 0.0f/ capacity);
return ratio >= this.loadFactor;
}
private void clear() {
Arrays.fill(this.buckets, null);
size = 0;
}
// 你给我一个key, 我看看这个key在map里面有没有,如果有我就get出来返回这个key对应的value
// 如果没有, 返回一个特殊值 null
/* 逻辑:
step 1: 先知道这个key在哪个bucket
step 2: 对这个bucket里有的这个linkedlist进行遍历,逐一判断到底有没有我要找的key
index 0
Entry array: Entry(key, value), Entry(key2, value2), ...
|
Entry(key3, value3) Entry(key4, value4)
*/
public Integer get(String key) {
int index = getIndex(key);
Entry<String, Integer> head = buckets[index];
Entry<String, Integer> cur = head;
while (cur != null) {
if (equalsKey(cur.key, key)) {
return cur.value;
}
cur = cur.next;
}
return null;
}
// 你给我一个key,我看看这个key在map里面有没有,如果有我就返回true, 没有我就返回false
public boolean containsKey(String key) {
int index = getIndex(key);
Entry<String, Integer> head = buckets[index];
Entry<String, Integer> cur = head;
while (cur != null) {
if (equalsKey(cur.key, key)) {
return true;
}
cur = cur.next;
}
return false;
}
// key 是null,不代表value是null,map允许key和value是null
// value pair: <null, 3>
/* 逻辑
我们尝试往hashmap里放入<key, value>这个entry
case1:以前map里没有这个entry
insert this entry to the head of the linkedlist in the bucktes
(get the linkedlist and insert head)
case2: 以前map里有这个entry
update oldkey's value to newValue
returnType:
如果你抹掉了旧的value <3,5> --> <3,3> 你一定把抹掉的value return出来
如果你没有抹掉任何值,return null就ok了
关系: entry -- Node 包含了key value field
put这个方法有没有可能触发rehash?注意有可能
*/
private Integer put(String key, int value){
// step 1: 先拿到key应该在的index
int index = getIndex(key);
Entry<String, Integer> head = buckets[index];
Entry<String, Integer> cur = head;
// step 2: 得看看以前map里面有可以这个key
while (cur != null) {
if (equalsKey(cur.key, key)) {
// update value
int oldValue = cur.value;
cur.value = value;
return oldValue;
}
cur = cur.next;
}
//代码执行到这里的时候说明map里面没有这个key-value pair需要insert at head
Entry<String, Integer> newEntry = new Entry<>(key, value);
// insert head
newEntry.next = head;
buckets[index] = newEntry;
this.size++;
//已经成功插入一个新的entry,有可能你的ratio要rehash
if (needRehahsing()) {
rehashing();
}
return null;
}
/*
rehash的逻辑不是说把以前旧的每个bucket里的linkedlist超过去就可以了
把以前的map里的每个entry都重新计算bucket重新放一遍
*/
private void rehashing(){
this.capacity *= 2;
/*
如果你自己定义扩容成几倍,scale_factor
private static final int SCALE_FACTOR = 4;
==> this.capacity *= SCALE_FACTOR;
*/
Entry<String, Integer>[] newBuckets = new Entry[this.capacity];
for (Entry<String, Integer> eachHead: buckets) {
Entry<String, Integer> eachCur = eachHead;
while (eachCur != null) {
Entry<String, Integer> next = eachCur.next;
eachCur.next = null;
// 把eachCur放进新的map
// 算一下这个eachCur在新的map里放在哪里
int index = getIndex(eachCur.key);
if (newBuckets[index] == null) {
newBucktes[index] = eachCur;
} else {
// insert head
eachCur.next = newBucks[index];
newBucks[index] = eachCur;
}
eachCur = next;
}
}
this.buckets = newBuckets;
}
/* function能做什么
你给我一个key,如果这个map里存在这个key,请你把它remove掉,并且返回你remove的key的value
如果不存在return null
逻辑:
还是得看看有没有这个key
step 1: 先找到index
step 2: 遍历一遍这个bucktets里的linkedlist看看有没有这个可以
如果有就remove掉
否则return null
*/
private Integer remove(String key){
int index = getIndex(key);
Entry<String, Integer> head = buckets[index];
Entry<String, Integer> cur = head;
Entry<String, Integer> prev = null;
while (cur != null) {
if (equalsKey(cur.key, key)) {
//如果正好是head
if (prev == null) {
buckets[index] = head.next;
} else {
prev.next = cur.next;
}
this.size--;
return cur.value;
}
prev = cur;
cur = cur.next;
}
return null;
}
}
Last updated