如果各位对不熟悉JAVA的 HashMap原理和实现,那么这篇文章可能值得一看。
HashMap 简介: 基于哈希表的 Map 接口的非同步实现。允许使用null值和null键。键不允许重复,值允许重复。存储是无序的,是按照哈希散列排序的。底层数据结构:Hash链表。
图示:
一 :实现原理(结合JDK源码片段):
1.初始化HashMap : 更具给定的参数初始化一个数据类型为Node<K,V>的table数组。
transient Node<K,V>[] table;
2.存入键值对<K,V>:
2.1 判断该table是否为空,若为空则初始化该table。
2.2 判断该key是否为null,若为null ,则将该<K,V>放置到数组的第一个位置。否则继续。
2.3 先使用hash(k) 计算得到该k对应的hash数值。
//计算key的 hash数值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
2.4 使用该hash 对整个表的长度进行取模运算,以求该k能放在数组中的坐标I 。
tab[i = (n - 1) & hash]
2.5判断table[i]处是否有数据,若不为空,则遍历该位置的链表,比较是否有相同的key,如果有相同的key则用的新的V覆盖旧的V,否则在该链表的顶部插入该节点。
判断是否为同一个key:
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
3.获取key对应的value。步骤和存入数据差不多。
二.MyHashMap的代码实现(肯定有不足之处):
import java.io.Serializable;
import java.util.*;
//zhaoke 2018-1-8
public class MyHashMap<K, V> implements Cloneable, Serializable {
//设置初始的容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//设置其最大容量为1024
static final int MAXIMUM_CAPACITY = 1 << 30;
//设置其容量阈值因子为0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//实际储存的大小
transient int size;
//阈值
int threshold;
//设置每一个节点 实现 Map.Entry<K,V> 是为了实现一个链表
static class Node<K, V> implements Map.Entry<K, V> {
final int hash;
final K key;
V value;
MyHashMap.Node<K, V> next;
Node(int hash, K key, V value, MyHashMap.Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final String toString() {
return key + "=" + value;
}
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value); // 调用 Objects的hashCode() 进行异或计算
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
//用一个数据类型为Node 的 数组充当 MyHashMap
private Node<K, V>[] table;
transient int modCount;
//计算key的 hash数值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//初始化MyHashmap
public MyHashMap() {
initTable();
}
//将数据插入MyHashMap中
public V put(K key, V value) {
//如果是空的,则进行初始化大小
if (table == null) {
initTable();
}
//允许K/V存放null
//若key为null则调用putForNullKey方法 放在数组第一位置
if (key == null)
return putNullKey(value);
//计算hash
int hash = hash(key);
//搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length);
//如果i处不为空,则循环遍历下一个元素,产生链表
for (Node<K, V> n = table[i]; n != null; n = n.next) {
Object k;
if (n.hash == hash && ((k = n.key) == key || key.equals(k))) {
V oldValue = n.value;
n.value = value;
return oldValue;
}
}
//如果i处索引为null或没有相同的,则表明还没有Entry
modCount++;
//插入,将k v插入到i处
addNode(hash, key, value, i);
return null;
}
void addNode(int hash, K key, V value, int bucketIndex) {
//若大小不够就扩充到原来的2倍
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
//创建新Node
createNode(hash, key, value, bucketIndex);
}
void createNode(int hash, K key, V value, int bucketIndex) {
//获取指定索引处的Node
Node<K, V> n = table[bucketIndex];
//将新创建的 Node放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry
table[bucketIndex] = new Node<>(hash, key, value, n);
//增加大小
size++;
}
//初始化table数组
private void initTable() {
table = new Node[DEFAULT_INITIAL_CAPACITY];
//初始化阈值
threshold=(int) Math.floor(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR);
}
//只放空数值
private V putNullKey(V value) {
Node oldnode = table[0];
Node node = new Node<K, V>(0, null, value, null);
table[0] = node;
return (oldnode == null) ? null : ((V) oldnode.getValue());
}
//返回对应key的 在数组的位置 取代模 % 取余运算
static int indexFor(int h, int length) {
// length 必须为 2 的N 次
return h & (length - 1);
}
//获取该MyHashMap的size
public int size() {
return size;
}
//判断该MyHashMap是否为空
public boolean isEmpty() {
return size == 0;
}
//重新修改起容量 新的容量
void resize(int newCapacity) {
//引用扩容前的Node数组
Node[] oldTable = table;
int oldCapacity = oldTable.length;
//扩容前的数组大小如果已经达到最大(2^30)了 1G
if (oldCapacity == MAXIMUM_CAPACITY) {
//修改阈值为int的最大值(2^31-1),这样以后就不会扩容了
threshold = Integer.MAX_VALUE;
return;
}
//初始化一个新的Node数组
Node[] newTable = new Node[newCapacity];
//将数据转移到新的Node数组里
transfer(newTable);
//将新table 赋值给原来的table
table = newTable;
threshold = (int)(newCapacity * DEFAULT_LOAD_FACTOR);//修改阈值
}
void transfer(Node[] newTable) {
//oldtable 引用原来的table
Node[] oldtable = table;
int newCapacity = newTable.length;
//遍历原来的table
for (int j = 0; j < oldtable.length; j++) {
//取得旧oldtable数组的每个元素
Node<K,V> n = oldtable[j];
if (n != null) {
//释放我们可爱的oldtable
oldtable[j] = null;
do {
Node<K,V> next = n.next;
//!!重新计算每个元素在数组中的位置
int i = indexFor(n.hash, newCapacity);
//此处注意 越最近添加的k 对应的节点 其距离 数组越进 可以理解为链表的头插
n.next = newTable[i];
//将元素放在数组上
newTable[i] = n;
//访问下一个Node链上的元素
n = next;
} while (n != null);
}
}
}
//获取数值
public V get(K key) {
if (key == null)
return getForNullKey();
Node<K,V> node = getNode(key);
return null == node ? null : node.getValue();
}
public Node getNode(K key){
//如果是空的 则返回null
if (table == null) {
return null;
}
int hash = hash(key);
//搜索指定hash值在对应table中的索引。
int i = indexFor(hash, table.length);
//如果i处不为空,则循环遍历下一个元素
for (Node<K, V> n = table[i]; n != null; n = n.next) {
Object k;
if (n.hash == hash && ((k = n.key) == key || key.equals(k))) {
return n;
}
}
return null;
}
//获取key为Null的value
public V getForNullKey(){
return (table[0]==null)?null:(table[0].value);
}
}