hashmap源码相关

哈希函数

  • 将任意长的输入映射成固定长的输出的比特串

h:{0.1}^* -> {0,1}^n

  • 计算机科学领域:
    • 表示将关键字的集合映射到地址集合上,实现记录的快速查找
  • 在密码学中:
    • 将任意长度的消息压缩成固定长度的哈希值(称为消息摘要或数字指纹签名等)

jdk1.7中hashmap组成

  • 数组+链表
  • 加入新值:
    • 根据key值获取一个哈希值
    • 由哈希值与当前数组大小取模确定放入位置
    • 在对应位置放入数组元素对应的链表中 更改原元素对应链表的头链指向(新加入的值存在在该链表的头链) 完成加入新值
  • 获取值:
    • 根据key值获取对应的哈希值x
    • 由获取到的哈希值x以及数组大小 找到数组中的元素
    • 遍历该元素下的链表 当里面某个元素的哈希值等于x时返回该元素 否则返回null

jdk1.8中hashmap组成

  • 数组+红黑树
    • 弥补1.7中使用链表的不足:即便哈希映射 难免会造成某个链表过于长 导致查询效率降低
    • 红黑树 当某个链表长度大于8时 该链表将自动被触发改为红黑树(查找效率log2n,也叫自平衡二叉查找树)
  • 加入新值:
    • 根据key值获取一个哈希值
    • 由哈希值与当前数组大小取模确定放入位置
    • 判断对应位置元素是否是树结点
    • 如果不是树节点 则是链表 则
    • 在对应位置放入数组元素对应的链表中 移动指向到链尾 并计数器加一 然后更改原链尾元素的next指向(新加入的值存在在该链表的链尾) 完成加入新值 若加入后计数器大于8 则进行链表红黑树化
    • 如果是树结点 按照红黑树的规则加入新元素
    • 不管是树或者链表 因为新加入元素在链尾 因此扩容时不用担心多线程下形成死循环问题 不会造成死锁(扩容时依旧不改变元素顺序

数组容量有关

  • 当使用hashmap时传入初始数组容量的值 实际的容量值会是大于传入值的第一个2次方数(调了源码内部函数获取)
  • 所有的2次方数-1得到的数x 对应的源码都是前4n位为0后4n位全为1 这样进行异或运算 就可以得到被运算数的后几位源码值z
  • 上述性质 就是很多时候要使用2次方数 然后减一进行异或的原因
  • 若要让前4n位也参与到运算哈希 则把x右移4n位得到y 再用y与z进行一次异或 这样就可以充分运用到原数据了

hashmap扩容

  • 当当前数组大小>阈(yu)值(=当前数组容量乘以装载因子0.75)时开始扩容
  • 使用双重循环 运用与jvm中的复制标记方法类似思想 先申请足够大的空间 再逐个放入 先数组元素 再每个元素下对应的链表
  • 在jdk1.7下 当使用多线程进行扩容时 可能会导致移动链表全部元素时 产生循环链表造成死循环(扩容前后链表元素顺序会被掉头 next指向可能会混乱) 然后链表元素总移不完 形成“死锁”

concurrenthashmap

  • 像hashmap的升级版 保证并发安全
  • jdk1.7:
    • 在hashmap基础上 加了分段锁 即在数组之上 相邻两个数组元素归为一个segment 整个ConcurrentHashMap由segment数组组成
    • segment继承了ReentranLock 故自带了一把可冲入锁 单个segment是一个小型的hashmap
    • 这样保证那下面的两个链表或者树的操作线程安全 同时 锁的粒度不至于过大(如hashtable 在每个方法上加锁 粒度太大) 影响效率
  • jdk1.8:
    • 在hashmap基础上 考虑到每个线程中的put操作 都要访问数组元素 再访问旗下的链表或者树(对头节点加锁了)
    • 在put方法中 对数组元素加锁 这样就可以更细化粒度 也保证并发安全
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值