java-HasnMap
概念
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的优化
- 单个链表长度超过8,数组长度达到64,会采用红黑树结构进行树化,优化查询效率
- 数组扩容时旧数据的迁移采用位运算 得到的值 为0或1 0位置不变 1位置为当前位置加原数组长度,避免重新hash的性能开销
- 1.7hashmap扩容时,采用的是头插法 有可能产生环,1.8后扩容采用的是尾插法,不会产生环
hashmap的线程安全问题
hashmap有线程安全问题,如果想使用线程安全的hashmap可以使用:
HashTable: hashtable 是在方法上加 synchronized关键字保证安全,性能较差
ConcurrentHashMap: 采用分段锁的方式保证线程安全,性能更好,推荐使用