HashMap解析并DIY一个MyHashMap
HashMap解析
看过很多对于HashMap的实现原理的资料,现在自己总结一下。
实现原理
- 数组+单向链表+二叉树(JDK1.8修改为当链表长度大于等于8时,将链表转为二叉树)
- 通过计算hash值得到元素在数组中的位置,如果hash冲突了,再判断key是否相等来确定替换值还是插入元素。
- JDK1.8之前是在链表头部插入元素的,在1.8中是插入尾部的。
- 当元素个数超过table.length * DEFAULT_LOAD_FACTOR(160.75=12)的时候,就把数组的大小扩大一倍(216=32),然后重新计算每个元素在数组中的位置。
数据结构图
DIY一个MyHashMap
这里只是按自己的理解简单的做了一个实现,用代码加强自己对HashMap的理解。
package com.liutc.demo.map;
/**
* @Author liutc
* @Date 2020-05-24
*/
public class MyHashMap<K, V> {
private static final int DEFAULT_CAPACITY = 16;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private Node<K, V>[] table;
private int size;
public MyHashMap() {
this.table = new Node[DEFAULT_CAPACITY];
}
/**
* 扩容
*/
private void grew() {
//当元素个数超过table.length * DEFAULT_LOAD_FACTOR(16*0.75=12)的时候,就把数组的大小扩大一倍(2*16=32),然后重新计算每个元素在数组中的位置
if ((size + 1) == (table.length * DEFAULT_LOAD_FACTOR)) {
Node<K, V>[] oldTable = table;
table = new Node[table.length << 1];
size = 0;
for (int i = 0; i < oldTable.length; i++) {
Node<K, V> node = oldTable[i];
while (node != null) {
put(node.key, node.value);
node = node.next;
}
}
}
}
/**
* 添加元素
* @param key
* @param value
*/
public void put(K key, V value) {
grew();
int hash = hash(key);
int index = calcIndex(hash);
Node<K, V> node = table[index];
Node<K, V> newNode = new Node<>(hash, key, value, null);
if (node == null) {
table[index] = newNode;
} else {
boolean keyRepeat = false;
Node<K, V> last = null;
while (node != null) {
if (node.key.equals(key)) {
keyRepeat = true;
node.value = value;
break;
} else {
last = node;
node = node.next;
}
}
if (!keyRepeat) {
last.next = newNode;
}
}
size++;
}
/**
* 根据key获取value
* @param key
* @return
*/
public V get(K key) {
int hash = hash(key);
int index = calcIndex(hash);
Node<K, V> node = table[index];
while (node != null) {
if (node.key.equals(key)) {
return node.value;
} else {
node = node.next;
}
}
return null;
}
/**
* 根据key删除元素
* @param key
* @return
*/
public V remove(K key) {
int hash = hash(key);
int index = calcIndex(hash);
Node<K, V> node = table[index];
//如果元素在头节点
if (node != null && node.key.equals(key)) {
Node<K, V> next = node.next;
//将下一个节点设为头节点
table[index] = next;
size--;
return node.value;
} else {
//不在头节点,需要断链重连
Node<K, V> pre = node;
node = node.next;
while (node != null) {
if (node.key.equals(key)) {
Node<K, V> next = node.next;
pre.next = next;
size--;
return node.value;
} else {
pre = node;
node = node.next;
}
}
}
return null;
}
/**
* 返回大小
* @return
*/
public int size() {
return size;
}
/**
* key的hash值
* @param key
* @return
*/
private int hash(Object key) {
if (key == null) {
return 0;
}
int h = key.hashCode();
//用key的hashCode与此hashCode无符号右移16位的值取位异或
h = h ^ (h >>> 16);
return h;
}
/**
* 根据hash值计算所在数组下标索引
* @param hash
* @return
*/
private int calcIndex(int hash) {
//当前数组长度-1与key的hash值进行与运算
return (table.length - 1) & hash;
}
/**
* 节点内部类
* @param <K>
* @param <V>
*/
private static class Node<K, V> {
//hash值
int hash;
K key;
V value;
//下一个节点
Node<K, V> next;
public Node(int hash, K key, V value, Node<K, V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
for (Node<K, V> node : table) {
while (node != null) {
sb.append(node.key + "=" + node.value + ",");
node = node.next;
}
}
sb.setCharAt(sb.length() - 1, '}');
return sb.toString();
}
}