众说周知,在jdk1.7之前HashMap的底层结构是由数组+链表构成的,而在jdk1.8之后HashMap的底层结构变为数组+链表+红黑树,目的是为了解决单链表查询的深度问题,提高get(key)的速度。本文基于数组+链表,简单实现hashmap的put、get等方法。
Hash()
根据同一散列函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同。
两个不同的输入值,根据同一散列函数计算出的散列值相同的现象叫做碰撞。
hash()是减少hash碰撞、进行高效存取的关键。hashmap是可以存储 null的key和value的,null的值就存储在数组[0]上,进行异或操作都是让数组下标的位置更加随机和分布均匀,减少冲突。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Put()
在 HashMap 中, hashCode() 方法决定了对象会被放到哪个 bucket 里,当多个 对象的哈希值冲突时,equals() 方法决定了这些对象是否是 “ 同一个对象 ” 。当我们使用put(key,value) 方法添加数据时 , 首先会对 key 进行 hash 计算,再进行hash % DEFAULT_INITIAL_CAPACITY得到在数组中的存放位置;------- 如果该位置上为空 , 那就新建一个 node 对象 , 保存 key- value到该数组位置上 ;------- 如果计算出来的数组位置上有元素的话 ; 就沿着此数组位置对应的单链表上的结点一个个比较------如果遇到相同的key,就用新的 value 替 换掉旧的value------如果找不到相同的 key ,就新建一个 node 结点,保存 hash 值,key 和 value ,然后插入到此单链表的尾部。
public V put(K k, V v) {
Node<K, V> node;
int index, hash;
hash = hash(k);
index = hash % DEFAULT_INITIAL_CAPACITY;
node = new Node<>(hash, k, v, null);
// 首次put时创建数组
if (talbe == null || talbe.length == 0) {
this.talbe = new Node[DEFAULT_INITIAL_CAPACITY];
}
// 考虑扩容
if (size > talbe.length * DEFAULT_LOAD_FACTOR) {
resize();
}
// 数组该位置为空, 头部插入
if (talbe[index] == null) {
talbe[index] = node;
size++;
return v;
} else {
Node<K, V> head = talbe[index];
Node<K, V> tail = null;
while (head != null) {
// 如果hash值相同,就再根据equals判断
if (hash(head.key) == hash(k) && k.equals(head.key)) {
head.value = v;
return v;
}
tail = head;
head = head.next;
}
tail.next = node;
size++;
return v;
}
}
Get()
get()也类似,本质上都是先计算key的hash值,根据hash值计算出数组的存放位置,再通过equals()判断是否为同一个key,是则返回。
public V get(K key) {
int hash = hash(key);
int index = hash % DEFAULT_INITIAL_CAPACITY;
Node<K, V> first, e;
first = talbe[index];
if (first == null) {
return null; //头节点为空,直接返回null
} else if (first.hash == hash && first.key == key || (key != null && key.equals(first.key))) {
return first.value;//头节点就是,直接返回
} else if ((e = first.next) != null) {
while ((e = e.next) != null) {//顺着单链表查找
if (e.hash == hash && e.key == key || (key != null && key.equals(e.key))) {
return e.value;
}
}
}
return null;
}
完整代码
接口类MyMap
public interface MyMap<K,V> {
int size();
boolean isEmpty();
V get(K key);
V put(K k,V v);
V remove(K k);
V update(K k,V v);
interface Entry<K,V>{
K getKey();
V getValue();
}
}
实现类MyHashMap
import java.util.*;
public class MyHashMap<K, V> implements MyMap<K, V> {
// 默认容量: 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 默认加载因子: 0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 数组
Node[] talbe;
int size;
public MyHashMap() {
this.size = 0;
}
//定义Node节点内部类,存储key-value
static class Node<K, V> implements MyMap.Entry<K, V> {
int hash;
K key;
V value;
Node<K, V> next;
public Node(int hash, K key, V value, Node next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public V get(K key) {
int hash = hash(key);
int index = hash % DEFAULT_INITIAL_CAPACITY;
Node<K, V> first, e;
first = talbe[index];
if (first == null) {
return null; //头节点为空,直接返回null
} else if (first.hash == hash && first.key == key || (key != null && key.equals(first.key))) {
return first.value;//头节点就是,直接返回
} else if ((e = first.next) != null) {
while ((e = e.next) != null) {//顺着单链表查找
if (e.hash == hash && e.key == key || (key != null && key.equals(e.key))) {
return e.value;
}
}
}
return null;
}
@Override
public V put(K k, V v) {
Node<K, V> node;
int index, hash;
hash = hash(k);
index = hash % DEFAULT_INITIAL_CAPACITY;
node = new Node<>(hash, k, v, null);
// 首次put时创建数组
if (talbe == null || talbe.length == 0) {
this.talbe = new Node[DEFAULT_INITIAL_CAPACITY];
}
// 考虑扩容
if (size > talbe.length * DEFAULT_LOAD_FACTOR) {
resize();
}
// 数组该位置为空, 头部插入
if (talbe[index] == null) {
talbe[index] = node;
size++;
return v;
} else {
Node<K, V> head = talbe[index];
Node<K, V> tail = null;
while (head != null) {
// 如果hash值相同,就再根据equals判断
if (hash(head.key) == hash(k) && k.equals(head.key)) {
head.value = v;
return v;
}
tail = head;
head = head.next;
}
tail.next = node;
size++;
return v;
}
}
@Override
public V remove(K key) {
int hash = hash(key);
int index = hash % DEFAULT_INITIAL_CAPACITY;
Node<K, V> nodes = talbe[index];
if (nodes != null) {
if (nodes.getKey().equals(key)) {
talbe[index] = nodes.next;
size--;
} else {
while (!nodes.next.getKey().equals(key)) {
nodes = nodes.next;
}
V value = nodes.next.getValue();
nodes.next = nodes.next.next;
size--;
return value;
}
}
return null;
}
@Override
public V update(K k, V v) {
return put(k, v);
}
public Set<K> Keyset() {
Set<K> kSet = new HashSet<>();
for (Node nodes : talbe) {
if (nodes != null) {
while (nodes != null) {
kSet.add((K) nodes.getKey());
nodes = nodes.next;
}
} else {
continue;
}
}
return kSet;
}
public Collection<V> values() {
ArrayList<V> values = new ArrayList<>();
for (Node nodes : talbe) {
if (nodes != null) {
while (nodes != null) {
values.add((V) nodes.getValue());
nodes = nodes.next;
}
} else {
continue;
}
}
return values;
}
public void print() {
for (Node node : talbe) {
Node nodes = node;
if (nodes != null) {
while (nodes != null) {
K key = (K) nodes.getKey();
V value = (V) nodes.getValue();
System.out.print(key + " : " + value + "\t");
nodes = nodes.next;
}
System.out.println();
}
}
}
private void resize() {
int newCapacity = talbe.length << 2;
Node<K, V>[] newTable = new Node[newCapacity];
newTable = Arrays.copyOf(talbe, talbe.length);
this.talbe = newTable;
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
}