定义
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable{static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认初始化容积,就是默认数组的长度为 16static final int MAXIMUM_CAPACITY = 1 << 30; //最大容积值,static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认负载因子值static final int TREEIFY_THRESHOLD = 8; //树化阈值static final int UNTREEIFY_THRESHOLD = 6; //树蜕化阈值static final int MIN_TREEIFY_CAPACITY = 64; //最小的树化容积
}
存储结构
每个 Node[] 数组中的元素被称为桶 bucket ,一个桶对应一个 hash 映射的值,例如 0 、 1 、 2 、 3 ,相同的 hash 映射值可能会出现不同的 key 值,因此针对桶采用链表结构存储 hash 映射值相同的所有数据【 JDK1.8 在单个链表长度大于阈值 8 时会自动转换位红黑树;当删除结点使某个红黑树中节点数小于阈值 6 时会自动从 红黑树蜕化成链表结构】
static class Node<K,V> implements Map.Entry<K,V> { // 是 Map 中所存储 Entry 的实现类final int hash; // 对应的 hash 值final K key; // 存储对应的 key 值V value; // 存储对应的 value 值Node<K,V> next; // 指向下一个节点的单向链
数据存储
transient Node<K,V>[] table;
相关参数 :
capacity :容积值标识当前输入的容量,始终保持是 2 的 n 次方,可以扩容,扩容后的数组大小为原始数组大小的 2 倍
loadFactor : 负载因子,默认是 0.75 ,注意这个值在使用链表法的实现中可以大于 1 ,负载因子越大越节约空间,但是导致 hash 冲突概率提高,链表的长度会增加。查询性能和空间浪费均衡下选取 0.75
threshold :扩容阈值,等于 capacity*loadFactor
底层实现
JDK1.7 版本:
hashmap 底层采用的是【 Entry 数组和链表】实现的。向一个链表中添加数据时采用的是头插法,在多线程并发操作中会有环形链的问题。
JDK1.8 版本 :
hashmap 使用 Node 类型的【数组和链表以及红黑树】实现的, Node 用于链表,红黑树使用的是 TreeNode树化处理 。 向一个链表中插入数据采用的是尾插法,可以避免环形链问题,但是并不是解决了线程安全问题,仍旧会有扩 容处理的 rehash 时的死循环问题、脏读丢失数据问题、 size 不准确问题。
实现原理
散列
Hash 哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系。散列表又称为哈希表。
散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中地址。
构造器
public HashMap() {//没有进行数组的初始化操作,只设置了负载因子值为0.75this.loadFactor = DEFAULT_LOAD_FACTOR;
}
public HashMap(int initialCapacity) {
//可以在创建 HashMap 对象时直接设置初始化容器值,负载因子默认0.75。this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {//并没有初始化数组if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);//如果初始化容积值小于0,则抛出运行时异常中断执行if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;//如果初始化容积值大于2的30次方,则初始化容积值为 2^30if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " + loadFactor);//负载因子值要求大于0,否则运行时异常。为了减少hash冲突的概率,一般建议小于1。this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);//根据传入的初始化容积值,获取新合法的容积值,该值应该是>=initialCapacity的最小2次幂。
}
static final int tableSizeFor(int cap) {int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
Put方法
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
//参数 1 获取 key 的新 hash 值
//参数 2 为 key
//参数 3为 value
//参数 4 为 true 不更改现有值 false 更改现有值,参数 5 为 false 则创建模式 true 追加模式
}
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}