最近看了一些面试题,感觉网上好像有挺多公司比较喜欢让面试者手写HashMap,说实话理解JDK HashMap源码还是很需要时间的。打开看了一下HashMap源代码,将近2400行代码,前两位作者乃大名鼎鼎Doug Lea、Josh Bloch,普通人确实无法匹及。好了,废话不多说了,今天写了一个迷你版HashMap,只包含put、get方法,当然这两个方法应该是HashMap中使用频率最高的两个了,理解了put、get方法,后续增加size、remove方法应该不难,同时这里也没有做多线程锁处理,因为是迷你版,尽量帮助理解即可。
下面是源码:
package cn.crabime.redis;
import java.util.ArrayList;
import java.util.List;
public class MyHashMap<K, V> implements MyMap<K, V> {
static class MyEntry<K, V> implements Entry<K, V> {
K k;
V v;
MyEntry<K, V> next;
public MyEntry(K k, V v, MyEntry<K, V> next) {
this.k = k;
this.v = v;
this.next = next;
}
public K getKey() {
return this.k;
}
public V getValue() {
return this.v;
}
}
private int defaultLength; //默认长度
private double defaultFactor;
private double useSize;
private MyEntry<K, V>[] table;
public MyHashMap() {
this(16, 0.75);
}
public MyHashMap(int defaultLength, double defaultAddFactor) {
this.defaultLength = defaultLength;
this.defaultFactor = defaultAddFactor;
if (defaultLength < 0) {
throw new IllegalArgumentException("传入长度不合理");
}
if (defaultAddFactor < 0) {
throw new IllegalArgumentException("传入负载因子不合理");
}
table = new MyEntry[defaultLength];
}
public V put(K k, V v) {
// 如果当前数组元素个数超过指定值,则需要扩容
if(useSize > defaultLength * defaultFactor) {
dilatation();
}
int index = getIndex(k, table.length);
System.out.println("k=[" + k + "],index=[" + index + "]");
// 拿到table数组中当前index值
MyEntry<K, V> currentEntry = table[index];
// 将当前k/v构建一个待插入的Entry
MyEntry<K, V> nextEntry = new MyEntry<K, V>(k, v, null);
if (currentEntry == null) {
table[index] = nextEntry;
useSize++;
} else {
if (currentEntry.getKey() == k || (currentEntry.getKey() != null && currentEntry.getKey().equals(k))) {
currentEntry.v = v;
} else {
while (currentEntry.next != null) {
if (currentEntry.next.getKey() == k || (currentEntry.next.getKey() != null && currentEntry.next.getKey().equals(k))) {
currentEntry.next.v = v;
break;
} else {
currentEntry = currentEntry.next;
}
}
if (currentEntry.next == null) {
currentEntry.next = nextEntry;
}
}
}
return nextEntry.getValue();
}
public V get(K k) {
int index = getIndex(k, table.length);
MyEntry<K, V> currentEntry = table[index];
if (currentEntry != null) {
// 从index处的链表中获取值
while (currentEntry != null) {
if (currentEntry.getKey() == k || (currentEntry.getKey() != null && currentEntry.getKey().equals(k))) {
return currentEntry.getValue();
} else {
currentEntry = currentEntry.next;
}
}
}
return null;
}
// 扩容
private void dilatation() {
System.out.println("开始进行扩容了...");
MyEntry<K, V>[] newTable = new MyEntry[defaultLength * 2];
List<MyEntry<K, V>> totalList = new ArrayList<MyEntry<K, V>>();
for (int i = 0; i < table.length; i++) {
if (table[i] == null) {
continue;
}
MyEntry<K, V> entry = table[i];
// 将所有的Entry添加到集合中
while (entry != null) {
totalList.add(entry);
entry = entry.next;
}
}
if (totalList.size() > 0) {
useSize = 0;
defaultLength = defaultLength * 2;
table = newTable;
for (MyEntry<K, V> myEntry : totalList) {
if (myEntry.next != null) {
myEntry.next = null;
}
put(myEntry.getKey(), myEntry.getValue());
}
}
}
private int getIndex(K k, int length) {
int m = length - 1;
// 这里用的位运算做取模操作,如果用%符号取模,那m=length而不是length-1
int index = hash(k) & m;
return index > 0 ? index : -index;
}
private int hash(K k) {
int h;
return k == null ? 0 : (h = k.hashCode()) ^ (h >>> 16);
}
public static void main(String[] args) {
MyHashMap<String, String> map = new MyHashMap<String, String>();
map.put("dDkMALcfRH", "5");
map.put("dbgheYdIdX", "14");
map.put("NxHFwVKWnk", "13");
map.put("DBBDCshUAH", "1");
map.put("ntJlqxeUBr", "9");
map.put("olQRqXGPvc", "9");
map.put("kVcpPJZnPx", "0");
map.put("pNofFlvYck", "3");
map.put("xmfOhdCmzX", "15");
map.put("sNLsIPKtvZ", "12");
map.put("JQFIaxJiub", "2");
map.put("kYUvuqybBq", "4");
map.put("dDvgWhskYh", "6");
map.put("pESrWwnmtG", "7");
map.put("NEElevYhuE", "8");
System.out.println("key=dDkMALcfRH, value=" + map.get("dDkMALcfRH"));
System.out.println("key=JQFIaxJiub, value=" + map.get("JQFIaxJiub"));
}
}
MyMap接口信息如下:
package cn.crabime.redis;
public interface MyMap<K, V> {
public V put(K k, V v);
public V get(K k);
interface Entry<K, V> {
public K getKey();
public V getValue();
}
}
输出结果如下:
k=[dDkMALcfRH],index=[5]
k=[dbgheYdIdX],index=[14]
k=[NxHFwVKWnk],index=[13]
k=[DBBDCshUAH],index=[1]
k=[ntJlqxeUBr],index=[9]
k=[olQRqXGPvc],index=[9]
k=[kVcpPJZnPx],index=[0]
k=[pNofFlvYck],index=[3]
k=[xmfOhdCmzX],index=[15]
k=[sNLsIPKtvZ],index=[12]
k=[JQFIaxJiub],index=[2]
k=[kYUvuqybBq],index=[4]
k=[dDvgWhskYh],index=[6]
k=[pESrWwnmtG],index=[7]
开始进行扩容了...
k=[ntJlqxeUBr],index=[9]
k=[NEElevYhuE],index=[8]