HashMap底层存储结构
HashMap是一个用于存储Key-Value键值对的集合,每一个键值对其实就是HashMap内部的Entry类对象。
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
Entry是HashMap的内部类,用来保存我们的键值,next指向下一个节点,hash用来保存key值的哈希码
HashMap它底层是基于数组和链表实现的数据存储结构。
HashMap在初始化时会创建一个默认长度为16的数组,当我们添加元素时它会根据key值的哈希码和数组长度取余得到元素在数组的存储位置。但是存在的问题就是不同的key值在经过计算之后可能会映射到相同的位置上,当插入一个元素时,发现该位置已经被占用,这时候就会产生冲突,也就是所谓的哈希冲突,所以HashMap结合链表正是解决了位置冲突问题。
HashMap 设置数组的每一个元素对应一个链表的头结点。当位置发生冲突时就往该链表的头部插入新的节点,新的节点指向旧的头结点。
HashMap存储数据的流程:
如上图: 当添加一个新的元素时先计算出元素在数组的存储下标,如果位置是空的直接插入到数组,如果位置不为空判断key值是否相等,相等则覆盖value值,不相等则历遍链表。历遍链表结束后key还是没找到则往链表的头部插入新的节点。
像上图数组第一个位置存放着一个Entry对象,当插入新Entry对象计算出的位置也是数组的第一个位置,这时候发生哈希冲突了。系统会把新的Entry插入到数组的第一个位置,并且新的Entry.next属性指向旧的Entry对象。
HashMap数据查找流程:
因为HashMap在内部维护这一个数组table,数组的每个位置保存着每个链表的表头结点,查找元素时,先通过hash函数得到key值对应的hash值,再根据hash值和数组长度计算得到在数组中的索引位置,拿到对应的链表的表头,最后去遍历这个链表,得到对应的value值。
put()方法源码解析:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
//计算key的哈希值
int hash = hash(key);
//根据key哈希值和数组长度计算出存储下标
int i = indexFor(hash, table.length);
//历遍table[i]整个链表,如果出现key重复的则覆盖value值,然后return结束程序
for