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

你要的hashMap解读来了

  最近快到金三银四了,不少小伙伴又开始忙忙碌碌找工作了,我也是,秉着为自己也为大家,整理了下hashMap的知识点,希望能对你有所帮助!
  这里主要是对jdk1.7和jdk1.8的分析  JDK1.7 hashMap结构图
  img  1.7 hashMap变量初始容量16  负载因子0.75  数组节点类型是  Entry  put函数过程 public V put(K key, V value) {             // 容量为空 则初始化         if (table == EMPTY_TABLE) {             inflateTable(threshold);         }         // key为null 则进行put key为null的操作         if (key == null)             return putForNullKey(value);             // 求hash值 根据hashCode 再进行一些位运算 降低hash冲突的概率         int hash = hash(key);             // 根据元素hash值 求索引         int i = indexFor(hash, table.length);         for (Entry e = table[i]; e != null; e = e.next) {             Object k;             // 如果元素hash值和key都和桶上的第一个元素相同,则替换value             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                 V oldValue = e.value;                 e.value = value;                 e.recordAccess(this);                 return oldValue;             }         }          modCount++;       // 添加元素 头插法(可能会造成死循环)         addEntry(hash, key, value, i);         return null;     } hash函数过程hash冲突的解决办法:
  开放定址法
  再哈希法
  链地址法(拉链法,hashMap使用)       /**      * Retrieve object hash code and applies a supplemental hash function to the      * result hash, which defends against poor quality hash functions.  This is      * critical because HashMap uses power-of-two length hash tables, that      * otherwise encounter collisions for hashCodes that do not differ      * in lower bits. Note: Null keys always map to hash 0, thus index 0.      */     final int hash(Object k) {         int h = hashSeed;         if (0 != h && k instanceof String) {             return sun.misc.Hashing.stringHash32((String) k);         }          h ^= k.hashCode();          // This function ensures that hashCodes that differ only by         // constant multiples at each bit position have a bounded         // number of collisions (approximately 8 at default load factor).         h ^= (h >>> 20) ^ (h >>> 12);         return h ^ (h >>> 7) ^ (h >>> 4);     }  求索引位置 indexFor函数// jdk1.7  求索引位置函数    static int indexFor(int h, int length) {         // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";         return h & (length-1);     } 扩容过程1.addEntry函数    /**      * Adds a new entry with the specified key, value and hash code to      * the specified bucket.  It is the responsibility of this      * method to resize the table if appropriate.      * 添加元素      * Subclass overrides this to alter the behavior of put method.      */     void addEntry(int hash, K key, V value, int bucketIndex) {         // 当前容量大于等于负载数 且当前位置首个元素不为空         if ((size >= threshold) && (null != table[bucketIndex])) {             // 扩容为原来的两倍             resize(2 * table.length);             hash = (null != key) ? hash(key) : 0;             bucketIndex = indexFor(hash, table.length);         }          createEntry(hash, key, value, bucketIndex);     } 2.resize函数    /**      * Rehashes the contents of this map into a new array with a      * larger capacity.  This method is called automatically when the      * number of keys in this map reaches its threshold.      *      * If current capacity is MAXIMUM_CAPACITY, this method does not      * resize the map, but sets threshold to Integer.MAX_VALUE.      * This has the effect of preventing future calls.      *      * @param newCapacity the new capacity, MUST be a power of two;      *        must be greater than current capacity unless current      *        capacity is MAXIMUM_CAPACITY (in which case value      *        is irrelevant).      */     void resize(int newCapacity) {         Entry[] oldTable = table;         int oldCapacity = oldTable.length;         if (oldCapacity == MAXIMUM_CAPACITY) {             threshold = Integer.MAX_VALUE;             return;         }            // 创建新数组         Entry[] newTable = new Entry[newCapacity];                  // 元素重新求索引位置 元素从旧数组移到新数组上         transfer(newTable, initHashSeedAsNeeded(newCapacity));         table = newTable;                  // 设置新的负载数         threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);     }      /**      * Transfers all entries from current table to newTable.      */     void transfer(Entry[] newTable, boolean rehash) {         int newCapacity = newTable.length;         for (Entry e : table) {             while(null != e) {                 Entry next = e.next;                 if (rehash) {                     e.hash = null == e.key ? 0 : hash(e.key);                 }                 int i = indexFor(e.hash, newCapacity);                 e.next = newTable[i];                 newTable[i] = e;                 e = next;             }         }     } 移除    /**      * Removes the mapping for the specified key from this map if present.      *      * @param  key key whose mapping is to be removed from the map      * @return the previous value associated with key, or      *         null if there was no mapping for key.      *         (A null return can also indicate that the map      *         previously associated null with key.)      */     public V remove(Object key) {                  // 移除key对应的元素 并返回entry         Entry e = removeEntryForKey(key);         return (e == null ? null : e.value);     }              /**      * Removes and returns the entry associated with the specified key      * in the HashMap.  Returns null if the HashMap contains no mapping      * for this key.      */     final Entry removeEntryForKey(Object key) {         if (size == 0) {             return null;         }         int hash = (key == null) ? 0 : hash(key);                  // 根据元素hash值求索引         int i = indexFor(hash, table.length);                  // 桶的首个节点         Entry prev = table[i];         Entry e = prev;          while (e != null) {             Entry next = e.next;             Object k;                          // 判断元素是否和移除的元素key和hash值相同 ,相同则进行移除操作             if (e.hash == hash &&                 ((k = e.key) == key || (key != null && key.equals(k)))) {                 modCount++;                 size--;                                  // 如果 桶的首个元素和被移除的元素相同 则将next 置为桶的首个元素                 if (prev == e)                     table[i] = next;                 else                     prev.next = next;                 e.recordRemoval(this);                 return e;             }             prev = e;             e = next;         }          return e;     } get函数(根据key获取entry)public V get(Object key) {     if (key == null)         return getForNullKey();     Entry entry = getEntry(key);      return null == entry ? null : entry.getValue(); }  final Entry getEntry(Object key) {     if (size == 0) {         return null;     }      int hash = (key == null) ? 0 : hash(key);          // 遍历索引上的链表     for (Entry e = table[indexFor(hash, table.length)];          e != null;          e = e.next) {         Object k;         // hash值相同 key也相等 则返回entry元素         if (e.hash == hash &&             ((k = e.key) == key || (key != null && key.equals(k))))             return e;     }     return null; } 缩容
  无  JDK1.8 hashMap1.8 hashMap 变量初始容量16  负载因子0.75  数组节点类型是  Node  TREEIFY_THRESHOLD 链表节点数大于 TREEIFY_THRESHOLD 转变为红黑树 image-20230202205319483  put函数过程    /**      * Implements Map.put and related methods.      *      * @param hash hash for key      * @param key the key      * @param value the value to put      * @param onlyIfAbsent if true, don"t change existing value      * @param evict if false, the table is in creation mode.      * @return previous value, or null if none      */     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;                          // 如果put的元素为桶的首个节点,e赋值             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) {                     if ((e = p.next) == null) {                         p.next = newNode(hash, key, value, null);                                                  // 链表长度大于 TREEIFY_THRESHOLD 链表转红黑树                         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;                 }             }                          // 统一处理,value值             if (e != null) { // existing mapping for key                 V oldValue = e.value;                 if (!onlyIfAbsent || oldValue == null)                     e.value = value;                 afterNodeAccess(e);                 return oldValue;             }         }         ++modCount;         // size大于负载*容量时,扩容         if (++size > threshold)             // 扩容             resize();         afterNodeInsertion(evict);         return null;     } hash函數    static final int hash(Object key) {         int h;                  // hashCode值 与 hashCode值右移16位做异或 得出来的值 高低位特征都有保留 扰动效果更好         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);     } 扩容过程 函数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;     }
  1.8的resize和1.7最大不同就是,元素重新求索引位置,不是单纯求 hash & (length-1),而是看元素key的hash值在newCap-1 的高位是1还是0,如果是1 元素索引位置+oldCap,如果是0元素索引位置保持不变。  get函数(和1.7类似)    final Node getNode(int hash, Object key) {         Node[] tab; Node first, e; int n; K k;         if ((tab = table) != null && (n = tab.length) > 0 &&             (first = tab[(n - 1) & hash]) != null) {             if (first.hash == hash && // always check first node                 ((k = first.key) == key || (key != null && key.equals(k))))                 return first;             if ((e = first.next) != null) {                 if (first instanceof TreeNode)                     return ((TreeNode)first).getTreeNode(hash, key);                 do {                     if (e.hash == hash &&                         ((k = e.key) == key || (key != null && key.equals(k))))                         return e;                 } while ((e = e.next) != null);             }         }         return null;     } 缩容
  注意当链表长度小于6时,红黑树会变为链表  面试考点JDK1.7,hashMap使用头插法为什么会造成死循环? void transfer(Entry[] newTable, boolean rehash) {         int newCapacity = newTable.length;         for (Entry e : table) {             while(null != e) {                 // 第一处                 Entry next = e.next;                 if (rehash) {                     e.hash = null == e.key ? 0 : hash(e.key);                 }                 int i = indexFor(e.hash, newCapacity);                 e.next = newTable[i];                 newTable[i] = e;                 e = next;             }         }     }
  原因是 多线程并发扩容时,假设A,B两个线程,当A线程执行完 第一处 ,时间片耗尽,线程B按照头插法,完成了完整扩容,此时链表相对于原来是逆序,A线程再继续顺序执行,会造成指针混乱,于是出现死循环。 为什么容量是2的指数幂?
  有两点,更好的得到新索引和 搭配数组的length-1 可以使 hash%(length) == hash & (length-1) 作用相等  更好的得到新索引,打个比方 容量如果一开始时 8 ,后面扩容成16 从二进制上看只有最高位不同(length最高位后面都是0),所以在扩容的时候,需要重新用元素的hash值和length-1求余 实际上只有最高位不同,其他位不变,修改的数据少了,提高了代码的执行效率(1.7这样写,但是没利用到,但是1.8利用了这个特点)  image-20230201115404578  如果扩容不是2的指数幂,那么扩容后的长度低位不会那么均匀,(试想下,如果低位有0的话,是不是每个hash值在这个位置都是0,大大提高了hash冲突的概率,是2的指数幂低位,length-1 低位全是1)  也能更好的降低hash冲突的概率  搭配数组的length-1 可以使 hash%(length) == hash & (length-1) 作用相等  如果使用自定义对象作为hashMap的key 为什么一定需要重写hashCode和equals方法?
  因为hashMap插入元素,会用到hashCode计算hash值 ,如果没有重写的话,默认使用Object的实现,即对象的内存地址 HashMapKey k1 = new HashMapKey(1); HashMapKey k2 = new HashMapKey(1); HashMap map = new HashMap<>(); map.put(k1, "test"); System.out.println("map.get(k2) : " + map.get(k2));
  上面的例子,如果沒有重写hashCode的话,k1和k2元素肯定不在数组的同一个位置上,因为hashCode使用的是内存地址,所以 map.get(k2)=null
  那为什么要重写equals呢?
  因为获取元素需要用到equals方法  HashMapKey k1 = new HashMapKey(1); HashMapKey k2 = new HashMapKey(1); HashMap map = new HashMap<>(); map.put(k1, "test"); System.out.println("map.get(k2) : " + map.get(k2));
  还是上面的例子,k1和k2,不管有没有重写hashCode 都有可能hash值相等(不同元素的hash可能相等),假设k1和k2的hash值相等,当根据key获取元素时,不但会判断元素的hash值还会判断key之间是否equals,如果没有重写equals方法(equals默认对象的内存地址),k1和k2的内存地址肯定不相同,所以 map.get(k2)=null
  总结:使用自定义对象作为hashMap的key 一定需要重写hashCode和equals方法,set集合比较,去重时(先比较hash,hash相同在比较equals),所以一样需要重写。  HashMap线程不安全的体现:JDK1.7 HashMap线程不安全体现在:死循环、数据丢失  JDK1.8 HashMap线程不安全体现在:数据覆盖  思考问题
  谈谈你理解的 HashMap,讲讲其中的 get put 过程。 1.8 做了什么优化? 是线程安全的嘛? 不安全会导致哪些问题? 如何解决?有没有线程安全的并发容器? ConcurrentHashMap 是如何实现的? 1.7、1.8 实现有何不同?为什么这么做?  引用
  https://crossoverjie.top/2018/07/23/java-senior/ConcurrentHashMap/#HashMap
  有问题,欢迎私信我哦

有没有男士可以用的护肤品推荐呀?现在无论是护肤还是化妆都不是女生的特权,现在的男生们甚至比女生们还会保养自己,比我们女孩儿还活的精致,可谓是真正的精致青年!Fresh馥蕾诗红茶紧致修护精华露RMB84050ML至澳门当铺的劳力士能买到真的吗?买到的绝对都是真的表,澳门还没有敢卖假表的当铺,但是我们不是专业人士,当铺的表有可能是,换过零件的表!!!还有就是,他上一任表主,肯定也是不太好,才把表留在了当铺!所以建议买新,这为什么要发掘关羽的坟墓?反对发掘关羽的坟墓。汉末三国的文物记载都比较详细了,没有必要为了挖掘而进行挖掘。不要打着考察的幌子,就可以为所欲为。有的东西一旦破坏,将留什么东西给子孙后代?适可而止,留有余量,才53岁的人,建筑行业做不了,没有其它技术,做什么好?有什么建议?沈理职谈的观点,53岁按说还没到退休年纪,按你的情况应该是没有稳定工作,也没有一技之长的人了。这个岁数很多公司因为你的年纪,都不愿意再招聘上岗了,上有老下有小,压力是非常大的,但是六七十年代,农民生病了怎么办?找谁看,要钱吗?谢邀我是四七年生人,六七十年代我已二十多三十岁了,在那个年代是伟大领袖毛主席领导的伟大时代,人民团结奋进,大搞社会主义基本建设,城乡建设曰新月异,人们安居乐业,夜不闭户路不拾遗。虽你吃过的最恶心的食物是什么?谢谢邀请。说到恶心的食物我还真是有一肚子的话要说。那就把今天的文章当做朋友饭后闲谈一样的吐吐槽吧。先说说国内的恶心食物吧。毛鸡蛋绝对算得上恶心了吧?虽然有很多人喜欢吃,不过那又是毛为什么拿相机对着电脑屏幕照相,照片会出现波纹?我们对着电脑屏幕拍照产生的波纹其实莫尔条纹(MoirPattern)。莫尔条纹是两条线或两个物体之间以恒定的角度和频率发生干涉的视觉结果,当人眼无法分辨这两条线或两个物体时,只能看电视剧突围中陆建设的党委代书记为什么排在董事长和副董后面?我可以很负责任地告诉你,这种情况根本就不会出现,这完全是编剧为了剧情需要自行臆断设计出来的。一国有企业中党组织的作用主流规章指出国有企业是中国特色社会主义的重要物质基础和政治基础,邻居小孩问WiFi密码,告知后邻居全家都在蹭网,该怎么办?我家移动宽带200兆应该快如飞剑,可事实是用起来既卡且慢,特别是晚上更让人头疼,查了一下起码有10台机器在蹭我家的网,換了几次密码,门一打开仍能看到墙根下有年青男女拿着手机,平板玩如今大学排行榜这么多,中国最好的十所大学究竟是哪些?中国最好的十所大学究竟是哪些?目前国内外各种各样的大学排行榜,他们基于各种考虑各种角度各种标准各种统计,所以排出来的名单也大为不同。首先,北京大学清华大学这两所学校是不用说的了,肯如果想上师范学校,是读公费师范好还是非公费师范好,为什么?谢悟空问答小秘书的邀请。如果想读师范类的院校应该选择公费还是非公费,这个并没有一个明确标准的答案,这个需要结合自己的家庭经济状况职业规划工作意向等考虑。第一,公费师范生可以享受各种
省专家组来我市开展中药材生态种植基地实地考察2月28日,由省农业农村厅省卫生健康委组成的省中药材生态种植基地评审专家组,来我市莒县库山乡开展实地考察工作。专家组一行先后到援康药业厂区库山乡瓜蒌生态种植基地进行现场考察,详细了70年前,中华人民共和国成立后制定的第一部选举法公布施行在全国范围内进行普选,依法产生各级人大代表,是召开全国人民代表大会及地方各级人民代表大会的重要前提。为使普选工作有法可依,尽快制定新中国第一部选举法就成为全国人大筹备工作的当务之急灵芝五指毛桃一直都相信中国的饮食疗法,自然吧多去看书,学习。掌握每一样的中药材的功效与作用了。每一样的保健和养生,都是我热爱和喜欢的学习啊。五指毛桃30g,先洗干净了,提前浸泡20分钟,让它充春季养肝喝什么茶?灵芝枸杞菊花茶疏肝明目提神乍暖还寒的天气,并不使人清爽。此时人体会出现诸多变化,如嘴巴苦眼睛干失眠提不起精神易发火等,而这些多与肝气不顺畅有关。素问六节藏象论指出肝为阳中之少阳,通于春气春季,万物生发正是养梅婷宽衣后的崩溃,黄渤性冷淡的麻木,将成人世界的不易都演透了哪个群体过得最不容易?那肯定是上有老下有小的中年人。中年人题材的作品在今年的电视剧市场如雨后春笋般出头,许多人都想要在这个领域分一杯羹。如今,又一部讲述中年人生活现状的电视剧走入我黑巧克力真的健康吗?黑巧克力可能是一种既美味又有益健康的食品。由可可树种子制成的黑巧克力是一种富含抗氧化物和矿物质的食物,可能有助于预防心脏疾病。通常意义来讲,黑巧克力的一般有如下优点。富有营养如果你健康手中握,didoG28S心电血糖手表全新再升级2015年2月8号,微信运动上线,随即带来的则是家喻户晓的步数统计,以往需要智能穿戴设备才能拥有的功能,通过一台智能手机即可实现,还记得当初大家都在比谁一天走得多,甚至还出现了一些用合理膳食解释清淡饮食存在的部分误区一提到清淡饮食,大家会联想到什么?身体不舒服了,去医院检查,最后医生大多都会说多喝水多运动吃清淡一点感冒发烧刚好,妈妈端来一碗白粥放在床头柜,反复叮嘱吃清淡点,趁热吃假期大餐吃的太狂飙力荐的陈皮,到底好在哪儿?狂飙剧中高启强最爱喝陈皮茶,无论是送礼会客,还是与人谈话,他都爱泡上一壶陈皮茶,甚至在某次宿醉之后,他也要用陈皮茶解酒。除此之外,他还劝人说,少点喝酒,多喝点老陈皮,理由是老陈皮对新会陈皮狂飙起来了,但是你真的了解吗?给新人几条实用建议早在古代,就有与陈皮相关的描述,并被记录在神农本草经和本草纲目中,被列为珍贵佳品。新会种植柑橘和取皮已有700多年的历史。早在宋朝,新会当地人就开始种柑橘取皮,慈禧太后就指定新会陈春天祛湿养脾胃,吃点陈皮食疗方陈皮又名橘皮,为芸香科植物橘及其栽培变种的成熟果皮。中医认为其性味辛苦温,入脾肺经有理气健脾,燥湿化痰之功,主治脾胃气滞证及痰湿壅滞证。陈皮药食同源,泡茶不仅香气四溢,沁人心脾,煲