map接口:
public interface IMyMap<K, V> {
V put(K key, V value);
V get(K key);
int size();
interface Entry<K, V>{};
}
实现:
import java.util.HashMap;
/**
* Created by hasee on 2017/11/2.
*/
public class MyHashMap<K, V> implements IMyMap<K, V> {
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //一定要是2的整数幂
Entry<K, V>[] table;
int size;
final float loadFactor;
int threshold;
public MyHashMap() {
loadFactor = DEFAULT_LOAD_FACTOR;
table = new Entry[DEFAULT_INITIAL_CAPACITY];
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
}
public MyHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public MyHashMap(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
int capacity = 1;
while (capacity < initialCapacity) //使得数组长度一定是2的整数幂
capacity <<= 1;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
}
@Override
public V put(K key, V value) {
int h = hash(key);
int index = indexFor(h, table.length);
//迭代此索引上的所有Entry对象
for (Entry<K, V> e = table[index]; null!=e; e=e.next){
//如果当前e的hash等于计算出来的hash,并且key也等于e的key,那么替换e的value并返回旧值
if (h == e.hash && key.equals(e.key)){
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
//如果索引上还没有对象或索引的链表没有此key,就创建对象
Entry<K, V> e = table[index];
table[index] = new Entry<K, V>(e, value, key, h);
//检测是否需要扩容
if (size++ >= threshold)
doubleSize();
return null;
}
//哈希扰动函数(提高性能的关键)
static final int hash(Object key){
// int h;
// return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
//上面两行是jdk源码,它等价于下面
if(key == null)
return 0;
//获取一个32位的hashCode
int h = key.hashCode();
//无符号右移16位(左边填充0),即如果当前hashCode小于2的16次方,temp就会等于0
int temp = h >>> 16;
/**
* 进行按位异或(XOR)运算,按位异或就是把两个数按二进制,相同就取0,不同就取1。
* 比如:0101 ^ 1110 的结果为 1011
* 任何一个数,与0按位异或的结果都是这个数本身:
* 0 ^ 0 = 0, 1 ^ 0 = 1
* 所以这里的含义是保留高16位,而低16位则会因XOR运算得出不同的数值
* 这样做的好处是:
* 自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性
* 从而降低哈希冲突
* */
int newHash = h ^ temp;
return newHash;
}
/**
* 这里正好解释了数组长度为什么一定要是2的整次幂
* 因为这样数组长度-1刚好能成为一个低位掩码
* 任何数与数组长度-1做与运算,相当于做取模(%)运算,不过会比%运算效率高
* 例如数组长度为16,那么18和16的模相当于18 & (16-1)
* 0001 0010 & 0000 1111 = 0000 0010 = 十进制的2
*/
static final int indexFor(int hash, int tableLength){
return hash & (tableLength - 1);
}
//扩容为原来的两倍
final void doubleSize(){
int newCapacity = table.length << 1;
Entry<K, V>[] newTable = new Entry[newCapacity];
threshold = (int)(newCapacity * loadFactor);
//需要再散列
hashAgain(newTable);
table = newTable;
}
//再散列
final void hashAgain(Entry<K, V>[] newTable){
Entry<K, V>[] source = table;
int newCapacity = newTable.length;
for (int i=0; i<source.length; i++){
Entry<K, V> e = source[i];
if (null != e){
source[i] = null;
do {
Entry<K, V> next = e.next;
int index = indexFor(e.hash, newCapacity);
e.next = newTable[index];
newTable[index] = e;
e = next;
} while (null != e);
}
}
}
@Override
public V get(K key) {
int index = indexFor(hash(key), table.length);
int hash = hash(key);
Entry<K, V> e = table[index];
while (null != e){
if (hash == e.hash && (e.key == key || key.equals(e.key)) )
return e.value;
e = e.next;
}
return null;
}
@Override
public int size() {
return size;
}
private static class Entry<K, V> implements IMyMap.Entry{
Entry<K, V> next;
V value;
final K key;
final int hash;
public Entry(Entry<K, V> next, V value, K key, int hash) {
this.next = next;
this.value = value;
this.key = key;
this.hash = hash;
}
}
}