简介
一般集合和map的底层原理实现可能会被面试官问到.
参数
- DEFAULT_LOAD_FACTOR
static final float DEFAULT_LOAD_FACTOR = 0.75f; 负载因子
2. 门限
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold;
门限计算公式threshold = 数组长度 * 负载因子;
size
size 是HashMap中实习存在的键值对数量
/**
* The number of key-value mappings contained in this map.
*/
transient int size;
modCount
hashmap 发生结构变化的次数.
/**
* The number of times this HashMap has been structurally modified
* Structural modifications are those that change the number of mappings in
* the HashMap or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount;
INITIAL_CAPACITY
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
初始化容量为16.
为什么选用2的幂次
因为 比如16: 1 0000
16 - 1 = 0 1111
使用这个对hashcode产生的书进行与操作 可以得到16以内的数
hashCode 如何将高位也进行与操作 减少冲突
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
hashmap 1.7 采用头插法, 1.8 采用尾插法 为什么
因为每次都要查询链表是不是8个, 所以查询结束直接尾插法,效率较高
HashMap 发生死循环的一个重要原因是 JDK 1.7 时链表的插入是首部倒序插入的,而 JDK 1.8 时已经变成了尾部插入,有人把这个死循环的问题反馈给了 Sun 公司,但它们认为这不是一个问题,因为 HashMap 本身就是非线程安全的,如果要在多线程使用建议使用 ConcurrentHashMap 替代 HashMap,但面试中这个问题被问的频率比较高,所以在这里就特殊说明一下。
1.8 每次链表长度到8都会扩容吗
不会哦. 有一个64的参数, 如果没有达到64的话, 会先进行扩容.
TIPS
- hashmap 采用了数组 + 链表 + 红黑树 jdk.18的存储结构.
- hashmap 数组部分称为哈希桶. 当链表长度大于等于8时, 链表数据将以红黑树的形式进行存储, 当长度降到6时, 转成链表.
- 链表的时间复杂度为O(n)
- 红黑树的时间复杂度为O(log n)
- 当插入的对象大小超过临界值时, HashMap将新建一个桶数组并重新赋值. 此时, 桶数组是原来的两倍, 门限也是原来的两倍. 然后从新赋值