hashMap和线程安全

hashmap是一个key-value键值对的数据结构:

1、从结构上来讲在jdk1.8之前是用数组加链表的方式实现,jdk1.8加了红黑树

2、hashmap数组的默认初始长度是16;

3、hashmap数组只允许一个key为null,允许多个value为null

hashmap的内部实现:

hashmap是使用数组+链表+红黑树的形式实现的:

1、其中数组是一个一个Node[]数组,我们叫他hash桶数组,它上面存放的是key-value键值对的节点。

2、HashMap是用hash表来存储的,在hashmap里为解决hash冲突,使用链地址法,简单来说就是数组加链表的形式来解决,当数据被hash后,得到数组下标,把数据放在对应下表的链表中。

hashmap的方法实现

1、put方法说明:
put方法的第一步,就是计算出要put元素在hash桶数组中的索引位置;

得到索引位置需要三步
1、put元素key的hashcode值
2、高位运算就是用第一步得到的值hashcode,用hashcode的高16位和低16位进行异或操作
3、为了使hash桶数组元素分布更均匀,采用取模运算,取模运算就是用第二步得到的值和hash桶数组长度-1的值取与。这样得到的结果和传统取模运算结果一致,而且效率比取模运算高

jdk1.8中put方法的具体步骤

1、先判断hashmap是否为空,为空的话扩容

2、不为空计算出key的hash值 i,然后看table[i]是否为空

3、为空就直接插入

4、不为空判断当前位置的key和table[i]是否相同,相同就覆盖

5、不相同就查看table[i]是否是红黑树节点,如果是的话就用红黑树直接插入键值对

6、如果不是开始遍历链表插入,如果遇到重复值就覆盖,否则直接插入,如果链表长度大于8,转为红黑树结构,执行完成后看size是否大于阈(yu)值threshold,大于就扩容,否则直接结束。

2、get方法说明:就是计算出要获取元素的hash值,去对应位置取即可。

hashmap扩容机制

hashmap的扩容中主要进行两部:

1、第一步把数组长度变为原来的两倍,第二部把旧数组的元素重新计算hash插入到新数组中;

2、在jdk1.8时,不用重新计算hash,只用看看原来的hash值新增的一位是零还是1

  • 如果是1这个元素在新数组中的位置,是原数组的位置加原数组长度
  • 如果是零就插入到原数组中。
  • 扩容过程第二部一个非常重要的方法是transfer方法,采用头插法,把旧数组的元素插入到新数组中。

3、hashmap大小为什么是2的幂次方

  • 在计算插入元素在hash桶数组的索引时第三步,为了使元素分布的更加均匀,用取模操作
  • 但是传统取模操作效率低,然后优化成hashcode&(length-1),设置成2幂次方,是因为2的幂次方-1后的值每一位上都是1,然后与第二步计算出的h值与的时候,最终的结果只和key的hashcode值本身有关,这样不会造成空间浪费并且分布均匀
  • 如果不是2的幂次方,如果length不为2的幂,比如15。那么length-1的2进制就会变成1110。在hashcode为随机数的情况下,和1110做&操作。尾数永远为0。那么0001、1001、1101等尾数为1的位置就永远不可能被entry占用。这样会造成浪费,不随机等问题。

hashCode()和equals()方法

  • HashMap使用hashCode()和equals()方法来确定键值对的索引和元素是否相等,当根据键获取值的时候也会用到这两个方法。
  • 如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

多线程的安全问题:

hashMap和hashtable:

  • 1 、HashMap是非线程安全的,HashTable是线程安全的。
  • 2、HashMap的键和值都允许有null值存在,而HashTable则不行。
  • 3、因为线程安全的问题,HashMap效率比HashTable的要高。
  • 4、Hashtable是同步的,而HashMap不是。
  • 5、HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
    问题:
    一般现在不建议用HashTable

①是HashTable是遗留类,内部实现很多没优化和冗余。

②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

高并发下的线程安全:

线程锁:
1、Synchronized(java自带的关键字)

2、 lock 可重入锁 (可重入锁这个包java.util.concurrent.locks 底下有两个接口,分别对应两个类实现了这个两个接口: (表级锁和行级锁)

   (a)lock接口, 实现的类为:ReentrantLock类 可重入锁;

   (b)readwritelock接口,实现类为:ReentrantReadWriteLock 读写锁)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值