首页 > 其他分享 >HashMap和HashTable

HashMap和HashTable

时间:2024-09-24 10:52:05浏览次数:3  
标签:key hash hashMap 线程 HashTable 哈希 死循环 HashMap

HashMap

hashMap基于哈希表,底层结果由数组实现,添加到map里的元素以 key-value的形式存储在数组中,在数组中key-value已一个实体的形式存储, 也就是继承至map接口中的entry,下图是map源码enrty 既然hashMap是基于哈希表,就会出现一个问题,就是哈希值重复,专业术语叫哈希碰撞......在hashMap中,如果出现hash值重复的情况的话,就会为重复的hash值创建一个链表(hash值是通过key来计算的),让他们按照插入顺序排列在链表中,注意,如果hash值相同,插入的key相同,会覆盖原有value

hashMap允许存入null键,null值(null值只能有一个,存在数组的第一个位置)

hashMap没有顺序,而且还随着元素的插入而改变

hashMap随着元素的添加自动扩容(扩容是2的整数次幂,也就是2的n次方), 当数组长度大于临界值时,会触发扩容(临界值=加载因子*当前数组大小)

hashMap的加载因子是0.75, 是因为因子越大,发生哈希冲突的可能性越大,因子越小,元素越少,但是会浪费很多空间,所以通过官网计算0.75是两者均衡最佳数

hashMap里的key不能重复,重复key会覆盖

hashMap线程不安全

>>>>>>>>>>>>>>>>>>>>>>>>源码解析>>>>>>>>>>>>>>>>>>>>>>>>>>>

hashMap在第一次初始化时是一个空数组,在首次put方法中会创建一个默认16大小的数组

当length为2的N次方的话,一定为偶数,这样length-1则一定为奇数,而奇数转换成二进制的话,最后一位定为1(可以自己测试下),这样当我们的hash值 与 奇数 进行与运算时候,得到的结果即可能为奇数,也可能为偶数(取决于hash值),此时的散列性更好,出现哈希冲突的情况就比较低,也就是说HashMap中形成链表的可能性更低;

而当length为奇数时,length-1为偶数,转换成二进制的话最后一位是0。根据上面的与运算实现来看,当用0来进行运算时,得到的结果均为0,既无论hash值是多少,最终都只能是偶数。相比于上面来说,length是奇数相当于浪费掉Entry数组一半的空间,更容易出现哈希冲突,形成链表,进而影响整个HashMap的性能。

为什么说hashMap线程不安全

在jdk7中, 当两个线程同时进行操作时, 如果他们的hash值和 bucketIndex值相同, 第二个线程会覆盖第一个线程的值,而且jdk7还会发生死循环的情况,如果在内部遍历数组时没有为Null的元素,单项链表变成循环链表,就会一直死循环下去, jdk8优化了死循环的情况,但是线程问题依旧没有解决,如果两个线程的hash值相同,并且该位置数据是null, 在第一个线程还未进行数据插入时第二个线程插入数据,这时第一个线程会覆盖第二个线程的数据

为什么hashMap会进入死循环

在jdk1.8之前扩容会发生死循环, 假设有两个值, key1, key2 , 当有两个线程同时进行扩容操作时,当a线程进行到Entry<K,V> next = e.next时挂起, b线程进行操作扩容后完毕,这时值得顺序变为了key2, key1(因为赋值操作是从头部开始,重新赋值后会颠倒顺序),  这时a线程进行newTable赋值操作,e=key2, next=key1, 将key2放入a线程表后, e变成了key1, next=null,这时再次将key1放入a线程表中,就变成了a线程头结点是key1,b线程头结点是key2, 他们的next分别是key2和key1, 这时就形成了循环依赖, 进行get操作的时候就会发送死循环情况

在jdk1.8解决了通过扩容产生死循环的情况,jdk1.7在赋值时是从头结点开始, jdk8改成了尾结点,但是虽然说解决了因为扩容产生死循环的问题, 但是还是有其他死循环的情况的   (红黑树死循环!!!!!!)

如果根节点、爷爷节点、父节点、左叔叔节点、右叔叔节点、新插入的节点都是一个元素,证明当前循环的树只有一个值,并且永远不会退出,因为它满足两个判断条件

HashTable

hashTable是线程安全的, 是因为他的每一个方法都加上了 synchronized 锁,所以才说他是线程安全的, 每一个线程访问都会锁住整个哈希表,下一个线程必须等待该线程执行反比释放锁之后才可以, 这样的话线程是安全的, 但是效率属实是太低了

hashTable中key或者是value都不能是null

所以不推荐使用hashTable, 如果想要保证效率的同时又保证线程的安全,可以学习一下 ConcurrentHashMap,学习资料在并发包里

标签:key,hash,hashMap,线程,HashTable,哈希,死循环,HashMap
From: https://blog.csdn.net/Small_white_528/article/details/142483094

相关文章

  • Java集合类面试题:Map接口(链表、HashMap、红黑树)
    收集大量Java经典面试题目......
  • HashMap源码分析:如何实现一次put方法
    前情提示:&为按位与运算,将两个数转为二进制,当他们对应位置都为1的时候,得到的结果为,其他情况下结果为0例如:0000000000001011--->110000000000001001--->90000000000001001--->9即11&9=9 本篇主要介绍hashMap的构造方法、put方法相关内容相关成员变量介绍......
  • 深入理解ConcurrentHashMap
    HashMap为什么线程不安全put的不安全由于多线程对HashMap进行put操作,调用了HashMap的putVal(),具体原因:假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的;当线程A执行完第六行由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正......
  • 深入理解ConcurrentHashMap
    HashMap为什么线程不安全put的不安全由于多线程对HashMap进行put操作,调用了HashMap的putVal(),具体原因:假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的;当线程A执行完第六行由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正......
  • 一文搞定WeakHashMapE0
    写在前面在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就需要一定的删除机制,淘汰掉一些对象。这个时候可能很快就想到了各种Cache数据过期策略,目前也有一些优秀的包提供了功能丰富的Cache,比如Google的GuavaCache,它支持数据定期过期、LRU、LFU等策略,但它仍然有可能会导致有......
  • 一文搞定WeakHashMap
    写在前面在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就需要一定的删除机制,淘汰掉一些对象。这个时候可能很快就想到了各种Cache数据过期策略,目前也有一些优秀的包提供了功能丰富的Cache,比如Google的GuavaCache,它支持数据定期过期、LRU、LFU等策略,但它仍然有可能会导致有......
  • ZBlogPHP网站Leaked 1 hashtable iterators错误
    当遇到Z-BlogPHP报告“Leaked1hashtableiterators”错误时,这意味着在PHP脚本执行期间发生了内存泄漏问题,具体来说是在处理哈希表迭代时出现了问题。这个问题可能是由于PHP脚本中的编程错误导致的,或者是PHP自身的bug引起的。解决步骤1.更新PHP和Z-BlogPHP......
  • 【Java集合】HashMap
    哈希表        哈希表又叫散列表,或者映射、字典都是指哈希表,哈希表是通过关键码映射到数组的某个位置来访问的数据结构,实现这个映射的函数就是哈希函数,哈希表结合了数组和链表的优点,查找和插入操作的时间复杂度都是O(1)。        哈希表基于数组实现,哈希函数......
  • HashSet&HashMap
    一.哈希......
  • Java HashMap详解:源码分析、hash 原理、扩容机制、加载因子、线程不安全
    这篇文章将会详细透彻地讲清楚Java的HashMap,包括hash方法的原理、HashMap的扩容机制、HashMap的加载因子为什么是0.75而不是0.6、0.8,以及HashMap为什么是线程不安全的,基本上HashMap的常见面试题,都会在这一篇文章里讲明白。HashMap是Java中常用的数据结构之一......