当前位置: 首页 > 后端技术 > Java

JDK成长记8:HashMap的兄弟姐妹们

时间:2023-04-01 22:28:21 Java

LinkedHashMap的源码底层原理

LinkedHashMap的源码底层原理LinkedHashMap继承自HashMap,只是在其底层增加了一个链表来维护插入或访问顺序,使得LinkedHashMap顺序变化,如下图所示:从中可以看出上图中,LinkedHashMap继承了HashMap,多了两个成员变量,tail和head指针,同样使用Entry内部类继承了HashMap的Node内部类,并在原来的基础上增加了before和after指针。默认是按照插入的顺序,也就是put的顺序。LinkedHashMap在放元素的时候会记录指针,记录数组元素的插入顺序。通过重写HashMap的newNode()方法,在put方法中插入一个Entry后,Entry的before和last指针会像链表一样连接起来,LinkedHashMap的tail和head指针也会记录first和last元素。用链表记录插入顺序。至于访问顺序,你可以在后面的例子中提到。TreeMap的源码层原理/h3>
TreeMap的源代码底层原理同理TreeMap也是一个有序的Map,底层通过红黑树维护顺序,TreeMap支持自定义排序规则。普通叶节点。树一开始是空的,所以肯定是第一步,生成一个根节点,创建一个Entry,就结束了。这个Entry中有left、partent、right、color等变量。创建TreeMap时,您可以指定排序规则。默认不指定使用默认Key值的compare方法,排序后生成排序后的二叉树,然后调整为红黑树。而且它没有数组结构,是纯红黑树,获取的时候通过遍历红黑树获取元素。TreeMap在遍历时使用EntryIterator从小到大遍历,实现有序访问。HashTable、HashSet、LinkedHashSet、TreeSet底层原理
HashTable、HashSet、LinkedHashSet、TreeSet的底层原理最后说说为什么其他HashMap的师兄师姐这么说?因为HashTable和HashMap最核心的区别就是使用synchronized来保证线程安全,这点和Vector+ArrayList很相似。因为HashSet使用了HashMap,但是add方法里面的值都是newObject()。结合map的特点,同一个key只能有一个keyvalue。当放置地图时,它将散列地址数组中的相同键。该位置被移除,然后原始值被覆盖,因此该Set被删除了。默认是无序的。核心代码如下:publicbooleanadd(Ee){returnmap.put(e,PRESENT)==null;}由于LinkedHashSet继承了HashSet,此时HashSet可以通过使用LinkedHashMap来保证有序访问。TreeSet也是一样,默认是按照key-value的compare方式排序的。您可以自定义比较器。底层使用TreeMap。添加元素时,也是一个空的Object,去重了,但是TreeSet的访问是可以有序的。publicbooleanadd(Ee){returnmap.put(e,PRESENT)==null;}他们的源代码极其简单,没有什么可研究的。所以重点是,你理解前面的HashMap,LinkedHashMap,TreeMap是关键。**让我们看看使用这些集合的一些场景:LinkedHashMap应用:LRUMapinStorm</div>LinkedHashMap应用:在Storm中使用LRUMap之前,大家应该熟悉LinkedHashMap的插入顺序。在调用put方法时,通过链表记录插入顺序。但是LinkedHashMap也可以支持访问排序。按照get方法的访问顺序排序。比如:这个底层很像插入顺序,也是通过一个叫accessOrder的变量和HashMap的put方法以及get中重写reservation的方法来实现的。每访问一个元素,该元素就会被移动到链表的指针,而刚刚访问的元素又被移动到链表的尾部。基于这种机制,我们可以实现一个LRUMap,实现一个自动失效的LRU内存缓存Map,这样当元素数量超过缓存容量时,LRU可以保证最近最少访问的元素被移除。如果你看过storm的源码,你会看到有这么一个LRUMap实现:importjava.util.LinkedHashMap;importjava.util.Map;publicclassLRUMapextendsLinkedHashMap{私有intmaxSize;publicLRUMap(intmaxSize){super(maxSize+1,1.0f,true);this.maxSize=maxSize;}@OverrideprotectedbooleanremoveEldestEntry(finalMap.Entryelderest){returnsize()>maxSize;}}这个方法巧妙的基于LinkedHashMap的访问顺序,实现了一个LRUMap。首先通过maxSize限制缓存Map的大小,调用父类构造函数,扩展因子为1.0f,第三个入参表示accessOrder=true。覆盖removeEldestEntry。其次,通过LinkedHashMap,在get方法中,如果accessOrder为true,则get元素会被放到链表的末尾。确保Map缓存中最近访问的元素不会被LRU丢弃。get方法源码如下:publicVget(Objectkey){Nodee;如果((e=getNode(hash(key),key))==null)返回null;if(accessOrder)afterNodeAccess(e);returne.value;}可以看到有一个方法afterNodeAccess,通过它来控制LinkedList访问时单向链表的顺序,从而实现有序访问。在put方法中,有一个扩展入口afterNodeInsertion。HashMap默认此方法为空,不执行任何操作。但是LinkedHashMap实现了这个方法,它是由removeEldestEntry+accessOrder控制的。如果访问顺序参数为真,且头节点有元素,且满足removeEldestEntry,即size()>maxSize,且缓存大小达到上限maxSize,则执行removeNode操作,移除第一个元素链表的。第一个元素是最近最少访问的元素。如下图所示:TreeSet和TreeMap应用:HDFS中文软件租约时间维护
TreeSet和TreeMap应用:HDFS中的租约期限是在HDFS中的LeaseManager中维护的,它有一个非常经典的契约检查机制。对于所有合约,它们根据续订时间在TreeSet中排序。以后检查时,每次都选择最好的。查看旧合约,如果超过60分钟,超过则解除合约,查看倒数第二老的合约。如果最早的合约还没有到期,则意味着其他合约一定没有到期。使用这种方式,可以巧妙的避免说后台线程要定时遍历所有的合约,查看最新的续约时间。如果一个客户申请一个合约超过1小时,仍然会不续约,合约会自动解除,这对他来说是一个很重要的机制。如下图所示:好了,到这里,《JDK源码成长记》合集的知识基本就拿来给大家学习了。当然,一定要结合所学,自己不断的看源码分析思路,然后告诉同事,和他们一起讨论,这样才能更加精通。文章没看,80%还给我。你可以在评论区回复你使用这些合集遇到的场景,欢迎你的评论。也可以搜索一下自己的业务代码,看看使用了哪些集合类,如何使用,有没有什么隐患?在下一篇文章中,我会对章节进行总结,同时也会为大家提供一些常见的面试题,方便大家检验自己的学习成果。相信大家在掌握了源码原理之后,无论是看源码、面试还是应用,都可以游刃有余。金句甜点今天除了知识和技能的增长,再给大家带来一道金句甜品来结束我的今天分享:坚持的三大秘诀之一个性化的坚持除了前面提到的可视化和目标之外,最后一个就是个性化。每个人都喜欢的东西,不喜欢的东西不能强求,还是同样的例子,如果你想减肥,比如你不喜欢吃西兰花,虽然它是低热量的食物,真的很适合减脂有身材的时候吃,但是又不得不吃。前两天还好,能忍,但肯定坚持不了。你要找到适合自己的低热量食物,定制化调整,不喜欢的东西怎么能坚持呢?运动也一样,你就是不喜欢做俯卧撑,喜欢平板支撑,然后换你喜欢的,不能做Burbee跳,可以做半臂等等。。。个性化的重点是,比如当你不喜欢看成长故事,觉得浪费脑子的时候,你可以看今日头条、微博、朋友圈奖励自己,再看一个成长故事,然后奖励自己做自己喜欢的事情。你可以做一些重要的事情,而且你正在做你喜欢的事情。适当的个性化调整,也是让你不断前行,逐渐养成习惯的个性化。时间长了,相信你不做也会觉得不自在。所以,当你选择了自己可以坚持和喜欢的事情,成为一个好习惯,不断提醒自己这是值得的,你就一定能够坚持下去。记住坚持的秘诀。可视化、目标化和个性化,你可以试试。最后,看完源码可以饭后问问同事或者同学,也可以share出来告诉他。欢迎大家在评论区留言与我交流。(免责声明:JDK源码增长基于JDK1.8版本,部分章节会提及旧版本的特性)本文由博客发布平台OpenWrite发布!