java-HashMap(重点)

本文详细解读了JavaHashMap的概念、put和get方法、哈希碰撞处理、负载因子的作用,以及为何String和Integer适合作为键。还介绍了JDK1.8的优化措施和线程安全问题及其解决方案。
摘要由CSDN通过智能技术生成

概念

HashMap是基于hash表的map实现类,它可以接收null的键值,是非线程安全的,底层基于数组加链表实现,1.8后加入了红黑树,HashMap底层维护了长度为16的Entry数组(长度是可以指定),我们使用hashmap存储数据的时候 会根据** key的hashcode方法**计算出hash值 根据hash值及数组的长度取模能够计算出 要存入数组中的下标位置,数据里面的每个位置都称作bucket桶。一个桶有可能会存放多个Entry (hash碰撞) ,多个Entry会以单向链表形式存放。如果指定下标的bucket不存在单向链表,则直接存入。如果存在遍历单向链表,对比key 是否equals . 如果发现有相同的key, 覆盖原来的内容。 如果不存在则将当前的entry追加到单向链表中, 如果桶的使用达到一定数量会触发扩容,这个数量是根据负载因子 和 数组长度决定的 (数组长度 _负载因子 ),默认的负载因子为0.75 ,默认数组长度为16 乘以 _0.75=12,桶的使用超过12就会触发扩容 ,扩容会创建一个新的数组 长度为旧数组的2倍 ,并将旧数组中的数据迁入到新数组中
hashmap不是线程安全的,
如果多线程考虑使用hashTable 或 ConcurrentHashMap 或
Map m = Collections.synchronizeMap(hashMap)

put方法源码详解

如果存入的key为null 那这个Entry会存入到0位数组中
不为null 会计算key的hash值
根据hash 及 数组长度会计算出桶的下标
查看下标下是否有对应Entry链表,如果有 遍历该链表
对链表中Entry的key进行equals对比,如果结果为true替换
没有对比到对应的key
则会将新的Entry插入到链表的表头中

什么是哈希碰撞(hash碰撞)

指的是两个不同的key计算出相同的hashcode 称为hash碰撞
发生hash碰撞后,他们会存入相同的桶中

为什么负载因子要默认为0.75

HashMap负载因子为0.75是 空间和时间 成本的一种折中,
负载因子过小,扩容频率变高,空间使用率变低
负载因子过高,空间使用率变高,但hash碰撞增加,造成链表长度增加影响查询性能
使用时可根据需求更改负载因子

get方法详解

根据key的hash方法及数组的长度,找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。因此,设计HashMap的key类型时,如果使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择

扩容详解

插入新的Entry对象时 需要判断size是否大于等于 负载因子*数组长度,如果大于需要
先对数组进行扩容,扩容就是用一个新的大数组替换原来的小数组,并将原来数组中的值迁移到新的数组中

什么是哈希表?(hash表、散列表)

哈希表(HashTable)又叫做散列表,是根据关键码值(即键值对)而直接访问的数据结构。也就是说,它通过把关键码映射到表中一个位置来访问记录,以加快查找速度。这个映射函数就叫做散列(哈希)函数,存放记录的数组叫做散列表。在数据结构中,我们对两种数据结构应该会非常熟悉:数组与链表。数组的特点就是查找容易,插入删除困难;而链表的特点就是查找困难,但是插入删除容易。既然两者各有优缺点,那么我们就将两者的有点结合起来,让它查找容易,插入删除也会快起来。哈希表就是讲两者结合起来的产物

为什么String、Integer这样的类适合作为key

因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能

如果使用自定义的对象作为key要注意什么

一定要重写equals 及 hashcode的方法

jdk1.8对于hashmap的优化

  1. 单个链表长度超过8,数组长度达到64,会采用红黑树结构进行树化,优化查询效率
  2. 数组扩容时旧数据的迁移采用位运算 得到的值 为0或1 0位置不变 1位置为当前位置加原数组长度,避免重新hash的性能开销
  3. 1.7hashmap扩容时,采用的是头插法 有可能产生环,1.8后扩容采用的是尾插法,不会产生环

hashmap的线程安全问题

hashmap有线程安全问题,如果想使用线程安全的hashmap可以使用:
HashTable: hashtable 是在方法上加 synchronized关键字保证安全,性能较差
ConcurrentHashMap: 采用分段锁的方式保证线程安全,性能更好,推荐使用
image.png

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值