范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

HashMap详解

  讲解步骤基础知识 工作原理 关键代码 核心方法 基础知识
  数组结构 数组接口,在查询数据方面,具备优势
  链表结构 链表结构,在增删数据方面,具备优势
  红黑树结构 红黑树结构,在查询数据方面,数据量较大的时候,具备一定的优势
  什么是散列(哈希)表 散列表,顾名思义,就是将数据分布在不同的列 但是散列表并不是完全将数据分散在不同的列,而是按照某种规则,将具备同样规则的数据存储在同一列。 即具备相同规则的数据存储在同一列,规则不同的数据分布在不同的列。 这种规则最终的产生与哈希值有关。 这里需要注意的事,哈希值只是确定最后存储列的因素,也就是说不同的哈希值可能会存在同一列。
  什么是哈希值 哈希值简单的说,就是hashCode方法产生的值。 默认的hashCode方法是由其地址值最终产生一个哈希值。 由于HashMap中的元素是否存储是由键来决定,所以如果自定义的类需要存储在键,且想遵循自己的存储规则,需要重写HashCode方法 又因为Map集合的键是不能重复的,所以需要重写equals方法,定义去重规则。工作原理存储结构
  HashMap基于散列法,又称哈希法:数组+链表+红黑树。 HashMap需要同时存储一对键和值。 Map集合中提供了put(key, value)方法,所有的键和值会被封装到一个Entry实现类(Node)对象,存储到集合中。 在存储的过程中,会先通过hashCode()方法获取一个哈希值,并通过这个哈希值,与数组的长度进行一定的运算,得到一个索引值(存储的列) 在通过equals方法来判断这个元素是否已存在,不存在则存储在该列,若存储,则保留原来的数据。 存储在一列的数据,将以链表的形式,前后关联,这样有利于将来进行删除的时候提高效率。 但是如果一列的桶结构数据过多,就会导致查询的效率降低。 为了优化桶结构带来的问题,HashMap中会去检查,当一列的桶结构数据达到8个以上,就降这一列树化(转变为树结构)名词理解所有的数据都是以Node节点为单位。 hash值:哈希值,该方法内部提供了一个扰动函数------int hashCode()        扰动函数:用于产生哈希值,前16位与后16位做异或运算,提高低位随机性。------h = key.hashCode()) ^ (h >>> 16) 路由寻址:由数组长度与哈希值产进行与操作,产生最终的存储列(索引位置):(table.length-1)&node.hash Hash碰撞:哈希值如果相同,就会存储到相同的列。 链化:哈希值相同,就会存储在同系列,产生桶状结构,桶结构过长,查询数据低效。 红黑树:jdk8引入,类似于二叉树,可以避免过长的桶状结构扩容原理扩容:增加数组长度。目的在于解决数据过多,链化严重,默认以两倍的长度扩容。 ①一列添加第8+个元素,且数组长度小于64,会优先扩容。 ②一列添加第8+个元素,且数组长度达到64个,会优先树化。 ③添加元素后,若哈希表中元素总个数超过阈值(一个指定的值),会进行扩容。 ④扩容后,会重新根据数组长度和哈希值计算存储位置。关键代码核心字段static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  	默认数组大小 static final int MAXIMUM_CAPACITY = 1 << 30; 	数组最大长度 static final float DEFAULT_LOAD_FACTOR = 0.75f; 	默认负载因子 static final int TREEIFY_THRESHOLD = 8;		树化阈值 static final int UNTREEIFY_THRESHOLD = 6; 	树降级阈值 static final int MIN_TREEIFY_CAPACITY = 64;		树化阈值 transient Node[] table; 	哈希表 transient Set> entrySet; 	键值对对象集合 transient int size;		元素长度 transient int modCount; 	增删元素次数 int threshold;扩容阈值     扩容阈值=loadFactor*capacity final float loadFactor;    负载因子核心方法
  put-->putVal(存储数据) final V putVal(int hash, K key, V value, boolean onlyIfAbsent,                    boolean evict) {         Node[] tab; Node p; int n, i;     	//判断表是否为空或长度为0,若满足条件,则初始化表(体现了延迟加载)         if ((tab = table) == null || (n = tab.length) == 0)             n = (tab = resize()).length;     	//判断要添加的元素对应的列是否为空,若满足条件,则直接插入         if ((p = tab[i = (n - 1) & hash]) == null)             tab[i] = newNode(hash, key, value, null);         else {             Node e; K k;             //判断元素的哈希值与要存储列的键相同,则替换键对应的值             if (p.hash == hash &&                 ((k = p.key) == key || (key != null && key.equals(k))))                 e = p;             else if (p instanceof TreeNode)                 //如果当前节点是一个数结构节点,按照树结构存储新元素。                 e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);             else {                     for (int binCount = 0; ; ++binCount) {                     //遍历当前列的节点,判断如果当前节点超过8个节点,则将当前列转为树结构。                     if ((e = p.next) == null) {                         p.next = newNode(hash, key, value, null);                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                             treeifyBin(tab, hash);                         break;                     }                                        if (e.hash == hash &&                         ((k = e.key) == key || (key != null && key.equals(k))))                         break;                     p = e;                 }             }             //存在相同键,就值替换新值             if (e != null) { // existing mapping for key                 V oldValue = e.value;                 if (!onlyIfAbsent || oldValue == null)                     e.value = value;                 afterNodeAccess(e);                 return oldValue;             }         }     	//记录操作次数         ++modCount;     	//判断元素个数达到指定的阈值,则进行扩容操作。         if (++size > threshold)             resize();         afterNodeInsertion(evict);         return null;     }
  resize(扩容)  final Node[] resize() {         Node[] oldTab = table;         int oldCap = (oldTab == null) ? 0 : oldTab.length;         int oldThr = threshold;         int newCap, newThr = 0;         if (oldCap > 0) {             if (oldCap >= MAXIMUM_CAPACITY) {                 threshold = Integer.MAX_VALUE;                 return oldTab;             }             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&                      oldCap >= DEFAULT_INITIAL_CAPACITY)                 //修改新表的长度为旧表的两倍                 newThr = oldThr << 1; // double threshold         }         else if (oldThr > 0) // initial capacity was placed in threshold             newCap = oldThr;         else {               // zero initial threshold signifies using defaults             newCap = DEFAULT_INITIAL_CAPACITY;             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);         }         if (newThr == 0) {             float ft = (float)newCap * loadFactor;             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?                       (int)ft : Integer.MAX_VALUE);         }         threshold = newThr;         @SuppressWarnings({"rawtypes","unchecked"})             Node[] newTab = (Node[])new Node[newCap];         table = newTab;      	//将新表内容,重新计算位置后,放入新表         if (oldTab != null) {             for (int j = 0; j < oldCap; ++j) {                 Node e;                 if ((e = oldTab[j]) != null) {                     oldTab[j] = null;                     if (e.next == null)                         newTab[e.hash & (newCap - 1)] = e;                     else if (e instanceof TreeNode)                         ((TreeNode)e).split(this, newTab, j, oldCap);                     else { // preserve order                         Node loHead = null, loTail = null;                         Node hiHead = null, hiTail = null;                         Node next;                         do {                             next = e.next;                             if ((e.hash & oldCap) == 0) {                                 if (loTail == null)                                     loHead = e;                                 else                                     loTail.next = e;                                 loTail = e;                             }                             else {                                 if (hiTail == null)                                     hiHead = e;                                 else                                     hiTail.next = e;                                 hiTail = e;                             }                         } while ((e = next) != null);                         if (loTail != null) {                             loTail.next = null;                             newTab[j] = loHead;                         }                         if (hiTail != null) {                             hiTail.next = null;                             newTab[j + oldCap] = hiHead;                         }                     }                 }             }         }         return newTab;     }
  tableSizeFor(数组长度初始化) 二进制位运算 右移:二进制数据向右移动一位,最高位补原最高位值,原最低位舍弃。4>>1结果等于2    2>>1结果等于1   无符号右移:二进制数据向右移动一位,最高位补0,原最低位舍弃。4>>>1结果等于2    2>>>1结果等于1              无符号右移动,会确保移动后一定是一个正数。 左移:二进制数据向左移动一位,最低位补0,原最高位舍弃。举例:4<<1结果等于8   8<<1结果等于16      或:有1则1  1001|100结果为1100(12)      static final int tableSizeFor(int cap) {         //下列操作的最终目的保证了,最终的n值一定比cap大,且最接近满足+1后数组长度定义的数值(0,3,7,15,31,63...)         1001  100         int n = cap - 1;           n |= n >>> 1;          n |= n >>> 2;         n |= n >>> 4;         n |= n >>> 8;         n |= n >>> 16;         return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;     }

中国移动私人电话询要个人验证码,难道都不能质疑一下么?幾天前就收到要我報驗證碼,説是為了給我每月減輕月結流量費用我説每月產生多少费用該收多少就收多少費用,没必要對方悻悻地説哪是為您着想我就就我在外面勞動,回家考慮需要再回复吧,就掛斷了世界杯足球赛如果改为两年一届,对中国足球来说真是好事吗?不要太乐观,我是觉得,假设世界杯改成两年一届,对于中国足球来说,也不是啥好事,甚至,可能是更坏的事虽然中国足球早就没有继续退步的空间了!也别以为世界杯扩军或者增加举办频率,国足就一我儿子两岁三个月,说话还是口齿不清奶奶会说成摆摆有什么办法吗?没关系,不用着急,上学一学拼音就扳过来了,平时和孩子用普通话多聊天慢慢熏陶,就可以改回来,不能急燥吓唬孩子,他会不知所云,索性就不说话了,这样适得其反,我女儿小时候就大舌头,自己宠你觉得林志颖年轻的时候帅到了什么程度?说心里话,我从没觉得林志颖帅过。我说这话,可能很多人会反感我,觉得我是在黑他。其实我没有。你最多可以认为我不懂得欣赏他的美。我读中学时,他已经很网红,很多很多同龄人都关注他,视他为魔兽怀旧服金团中看到别人低价拿装备一定要抬价是什么心理?前两天看了句评论很适合我讨厌两种人,一种是捡漏的人,另一种是妨碍我捡漏的人。我们最终活成了我们讨厌的人流泪流泪流泪练个小猎人,第一个CD去mc就掉了叶子,100起,我出价,一个D嫌lol有哪些冷门的单挑王?单挑能力是在线上对拼以及41分带时即为关键重要的,如果单挑能力非常的强,难么我们绝对可以作为牵制点为队友提供便利,给对手制造麻烦。今天丁丁老师就来和大家聊一聊冷门单挑王第一位牧魂人高洪波做足协主席会怎么样?专业人士管理,中国足球会不会有起色?高洪波做足协主席会怎么样?专业人士管理,中国足球会不会有起色?很难说,中国足球特别是男足,世界上什么灵丹妙药,在其它国家都有用,而在中国失效,以前蔡振华任足协主席,有声音,他只是乒全运会女排江苏32战胜天津,如何评价张常宁的表现?可以说用完美二字来形容她的表现是再合适不过了,她应该是这场比赛江苏女排能够获胜的最大功臣了吧,那个我们熟悉的张常宁又回来了,中国女排绝对离不了张常宁,希望她不要早早退役!今天张常宁天津女排关键时刻掉链子,张常宁带队为何能击败李盈莹袁心玥?这场球从头至尾,张常宁打得很好,起到了核心作用,全能作用,尤其在决胜局1014落后的情况下,挽救6个赛点,并以1917获胜,用神奇逆转毫不为过。张常宁作为队长,靠的是全队团结拚搏,主要是玩单机游戏为主,预算3000是买个好显卡还是买ps4por?如果你爱好的游戏比较广泛,而且对PS4上的独占游戏不是很感冒的话,我觉得3000元的显卡比买PS4pro更划算一些,因为现在3000元能买到RTX2060或vega64这样级别的中华为都自研鸿蒙系统了,为什么还用安卓系统,究竟是什么原因?提问题前多过下脑子,不要以为打得出几个字就张口就来动动脑子不好吗,我国自己能生产粮食,为什么还要进口外国的?有北斗导航了,为什么还要用GPS而且与之兼容?生态的打造,最起码要保护开
湖人两首轮遭哄抢!乔丹儿子与皮蓬前妻约会,戈贝尔回应爵士重建德国男篮在施罗德与小瓦格纳的率领下,在格鲁吉亚进行的男篮欧锦赛小组赛中连续战胜了法国波黑立陶宛三支强队,不但提前晋级,还一跃成为本届欧锦赛的夺冠大热门。据美国八卦杂志TMZ爆料,乔李敏镐的同款粉红帆布鞋,超适合作为情侣鞋NikeWMNSAIRFORCE107LXNikeAF1近年推陈出新,不段推出设计新颖的独家款式,以嫩萤光橘与樱花粉为基底,采用跳跃式的撞色缝线作为设计重点,活力又温柔感的色调十分蒋介石心腹在襄阳被俘,与我党有血海深仇,毛主席处理方式真高明1948年,刘伯承大军在襄樊战役中大获全胜,并擒获了国民党高级领导康泽。康泽是国民党党内有名的特工,在国民党内的地位非常的高,他更是一个臭名昭著的特务头子。蒋介石的情报机构中华复兴币印(Poolin)交易所出现了提现困难情况,这是又要跑路了?交易所作为币圈食物链的顶端,安全以及运营出现问题都会导致交易所跑路。真正受损失的也是无辜的投资者。没有经历过真正牛熊市的交易所很难在这个市场存活,这也就是为什么不建议投资者选择小交生活在忙,别忘了身边的美已于而立之年,小儿两周,刚还清家里的债务,欲存款置房,母亲又病倒,看着身边的朋友都已房车齐全,更加让自己焦头烂额,原本觉得只要努力的自己,就好像可以掌握身边一切的时候,不免又觉得有火星氧气已够一名宇航员呼吸100分钟来源科学网MOXIE制氧装置随毅力号火星车着陆。图片来源NASAJPLCaltech2021年,美国宇航局(NASA)在火星上进行了制造氧气的小型实验,成功产生了可供呼吸约100分百里守约传说皮肤碎云上架,16款皮肤限时折扣,2大限时礼包来袭王者荣耀将在9月7日进行更新,这次更新有很多需要点券的活动,其中就有百里守约的新皮肤,以及皮肤折扣活动,下面一起来看看吧。百里守约传说皮肤碎云超人气英雄百里守约终于要上架他的首款传戊戌变法为什么会失败?一说起近代史,晚清的腐朽总是让人大呼荒唐,比如用军费举办生日庆典重修圆明园等事件都挺匪夷所思的,列强的坚船利炮都打到家门口了,依旧是滥用无度,歌舞升平。在这样的大环境下,却也还有有华为MATE50由维信诺京东方供货AMOLED屏幕华为MATE50由维信诺京东方供货AMOLED屏幕科创板日报6日讯,记者从产业链人士处获悉,华为Mate50系列将由国内OLED厂商维信诺和京东方供货柔性AMOLED屏幕,支持最高家有幼儿园的娃,每天只需十分钟,家长可陪出高质量数学宝宝大家好,我是果妈幼儿园娃的家长,现在有多卷,你们知道吗?01孩子算术好,不代表数学启蒙好果妈有一次遇到一个中班的小姑娘,20以内的加减法,算得特别溜。本以为这个小姑娘已经够厉害了,诺贝尔文学奖史上最大丑闻评委泄露名单,瑞典未来女王被人侵犯1896年12月10日,伟大的科学家诺贝尔与世长辞。诺贝尔生前拥有255项专利和遍布世界各地的工厂,遗产数目非常可观。诺贝尔在生命的最后几年,曾先后立下过3份遗嘱。最后生效的那份遗