Java基础之HashMap

HashMap 底层数据结构

数组 + 链表 + 红黑树

Hash算法

static final int hash(Object key) {
	int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

低16位与高16位进行异或运算,让其低16位同时保存了高16位的特点,避免Hash冲突
因寻址算法n - 1 的高16位一般都是0,也就是说实际地址只和key低16位有关,如果2个key低16位相同则容易产生Hash冲突,所以优化了高低16位异或运算

寻址算法

(n - 1) & hash(key)

数组长度n为2的n次方时,(n - 1) & hash(key) 相当于对数据取模运算,但是与运算效率更高

Hash冲突解决

链表 + 红黑树

链表节点源码

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
}

每一个节点都保存自身的hash,key,value和下一个节点

put值的时候,采用尾插法插入链表,链表达到一定长度进行树化,因为遍历链表时间复杂度是O(n) 遍历红黑树复杂度是log(n)

扩容优化

扩容为原来的2倍

	/**
	 * The default initial capacity - MUST be a power of two.
	 */
	static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

扩容时机:当前Capacity > Capacity * loadFactor

扩容步骤:
新建一个Entry空数组,长度是原来的2倍
遍历原数组,进行ReHash计算位置

将hash(key) 与扩容后的 n-1 进行 & 运算,如果多出1个bit的1,则新位置为index + oldCap否则不变

Java 1.8的改变

我们知道,HashMap底层是使用数组存储Key-Value这样的实例,Java7叫做Entry,Java8叫做Node,是因为Java8要支持红黑树

put值的改变

Java7采用头插入法,作者可能考虑到后put入的值访问概率更大,优化链表查询效率
Java8之后都是尾插入法了

多线程环境下,Java7 resize采用头插法,同一位置上新的元素总是在链表的头部位置,也就是A --> B resize过后可能会出现 B --> A 这样就形成了死循环
而Java8采用尾插入方式,不会改变链表顺序,保持之前的引用关系

虽然Java8之后多线程下不会产生死循环,但是get/set方法没有加锁,也无法保证put进去的值get出来后还是原值,所以还是线程不安全的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值