4-1 HashMap源码(2021-11-9)

1 Map接口和常用方法(jdk1.8)

  • 用于保存具有映射关系数据key-value双列元素。

  • Map中的key和value可以是任何类型元素,会封装到HashMap$Node对象中。

  • Map中的key不允许重复,有相同的key时,就替换。

  • Map中的key可以为null,value也可以为null;但是key只允许有一个null,value可以有多个。

  • 常用key作key,但是其他对象也可以。

  • Node实现了Entry接口。

2 Map遍历方式

//第一组:先 取出 所有 key ,通过 Key 取出对应的Value。
//最简单:
System.out.println("============第一组============");
Set keyset = map.keySet();
for(Object key : keyset){//增强forSystem.out.println(key + "-" + map.get(key));
}//迭代器
System.out.println("============第一组============");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {Object key =  iterator.next();System.out.println(key + "-" + map.get(key));
}//第二组:把所有的value取出。System.out.println("============第二组============");
Collection values = map.values();
//增强for
for(Object value : values){System.out.println(value);
}
//迭代器
System.out.println("============第二组============");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {Object value =  iterator2.next();System.out.println(value);
}//第三组:通过EntrySet 来获取 k-v。
System.out.println("============第三组============");
Set entrySet = map.entrySet();
for(Object entry : entrySet){Map.Entry m = (Map.Entry) entry;System.out.println(m.getKey() + "-" + m.getValue());
}System.out.println("============第三组============");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {Object entry = iterator3.next(); // NodeMap.Entry m = (Map.Entry) entry;//向下转型System.out.println(m.getKey() + "-" + m.getValue());
}

3 put方法(HashSet分析过源码)

  • 无参构造:初始化加载因子0.75;

  • put一个元素:

    • putVal(传入key的hash值,key,value)

      • 如果table是null 或者 长度 = 0 就扩容,第一次扩为16,阈值是12.

      • 如果第一处是null,直接加入。

      • else第一处不是null:

        • 如果hash值相同 并且 ( 是相同对象 || equals 相同 ):e=p。
        • else if :判断是不是树,如果是,按照红黑树的方法添加。(此处还没有深究)
        • else:后面跟的是链表:
          • 循环对比:

            • 如果下一处是null,就加入;再判断这条链表是否需要树化;break;
            • 如果hash值相同 并且 ( 是相同对象 || equals 相同 );break;
        • 如果e不是null:
          • 替换。
      • ++modCount;

      • if (++size > threshold) //判断是否到达阈值resize(); //扩容
        afterNodeInsertion(evict);
        return null; //执行成功!
        

4 resize方法

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold; // 确定阈值 12int newCap, newThr = 0;if (oldCap > 0) { // 如果 oldCap > 0if (oldCap >= MAXIMUM_CAPACITY) {  // >= 2乘1073741824 ?threshold = Integer.MAX_VALUE; // table最大值return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&  // ( newCap = oldCap*2 < 2乘1073741824) && (oldCap >= 16)oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // 新的阈值 = 旧的阈值 * 2}else if (oldThr > 0) // 啥时 候进来??newCap = oldThr; // 新容量 = 旧的阈值else {        // 0容量时,进入。newCap = DEFAULT_INITIAL_CAPACITY;  // 16newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 12}if (newThr == 0) { // 新的阈值 = 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<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab; // if (oldTab != null) { // oldTab != nullfor (int j = 0; j < oldCap; ++j) {Node<K,V> 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<K,V>)e).split(this, newTab, j, oldCap);else { // preserve orderNode<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.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; // 返回更新后的 table。
}

5 hash计算

  • 得到hash值——>得到索引值。
static final int hash(Object key) { //hash方法(key)int h;return (key == null) ? 0 : ( h = key.hashCode() ) ^ ( h >>> 16 ); //求出hashCode值。
}
// Object.java 中
public native int hashCode(); // 不知道

5-1 为什么h = key.hashCode()) 与 (h >>> 16) 异或?

  • 在jdk1.7中有indexFor(int h, int length)方法。jdk1.8里没有,但原理没变。1.8中用**tab[(n - 1) & hash]**代替但原理一样。
  • 1.7源码:
static int indexFor(int h, int length) {  // jdk1.7 得到下标方法。return h & (length-1); //返回的是 数组下表 = 索引值。
}
  • 原因总结:

    • 由于和(length-1)运算,length 绝大多数情况小于2的16次方。所以始终是hashcode 的低16位(甚至更低)参与运算。要是高16位也参与运算,会让得到的下标更加散列
    • 所以这样高16位是用不到的,如何让高16也参与运算呢?所以才有**hash(Object key)**方法。让他的hashCode()和自己的高16位^运算。
  • 例子:

  • 补充:

当length=8时 下标运算结果取决于哈希值的低三位。

当length=16时 下标运算结果取决于哈希值的低四位。

当length=32时 下标运算结果取决于哈希值的低五位

当length=2的N次方, 下标运算结果取决于哈希值的低N位。

6 remove方法

public V remove(Object key) { //按照key 删除元素Node<K,V> e;  // 创建新的结点return (e =  removeNode( hash(key) , key, null, false, true) ) == null ? null : e.value;// 没有这个key就返回null
}
final Node<K,V> removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) { //传入要删除的元素信息。Node<K,V>[] tab; //临时数组Node<K,V> p;  //临时Node元素int n, index;  // 临时变量if ((tab = table) != null && (n = tab.length) > 0 &&(p = tab[index = (n - 1) & hash]) != null) { // 如果数组有内容 并且 长度大于0 并且 要删除的key位置有内容Node<K,V> node = null, e; //临时NodeK k; V v; if (p.hash == hash &&  //如果第一个就找到了!!!node = p((k = p.key) == key || (key != null && key.equals(k))))node = p; //第一个就找到了!else if ((e = p.next) != null) { // else:if (p instanceof TreeNode) //是红黑树就这样:node = ((TreeNode<K,V>)p).getTreeNode(hash, key);else { //是链表:do { //循环:if (e.hash == hash && // 找到到相同的了((k = e.key) == key ||(key != null && key.equals(k)))) {node = e; //node break;  // 退出循环 }p = e; //后移。} while ((e = e.next) != null);//循环到尾巴!}}// 找到了node && if (node != null && (!matchValue || (v = node.value) == value||(value != null && value.equals(v))))      {if (node instanceof TreeNode) // 红黑树 去元素。((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);else if (node == p) // 不是红黑树,=第一处元素。tab[index] = node.next; // 第一处置空给。else // != 第一处元素p.next = node.next; // 前->后,中间断。++modCount;--size; // size-1。afterNodeRemoval(node);return node; // 返回 node}}return null;
}

7 get方法

public V get(Object key) { // 传入keyNode<K,V> e; // 临时Nodereturn (e =  getNode( hash(key), key ) ) == null ? null : e.value; // 没找到就返回null ;找到了就返回 值。
}
// getNode( hash(key), key )  返回的永远是一个对象(找不到就是null)。
final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; //临时 table Node<K,V> first, e; //临时Nodeint n; K k; // 临时变量if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {//有table && 长度>0 && 第一处有元素 if (first.hash == hash && // 检查第一处元素 是否相同 ((k = first.key) == key || (key != null && key.equals(k)))) return first; // 一次查看相同返回 first(Node)!!!!!幸运!if ((e = first.next) != null) { //如果尾巴还有,向下寻找!if (first instanceof TreeNode) // 如果是红黑树按照树的方式寻找。return ((TreeNode<K,V>)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;
}

集合—HashMap源码相关推荐

  1. Java集合:HashMap源码剖析

    一.HashMap概述 二.HashMap的数据结构 三.HashMap源码分析      1.关键属性      2.构造方法      3.存储数据      4.调整大小 5.数据读取     ...

  2. Java集合框架之三:HashMap源码解析

    Java集合框架之三:HashMap源码解析 版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! HashMap在我们的工作中应用的非常广泛,在工作面试中也经常会被问到,对于这样一个重要的集 ...

  3. hashmap删除指定key_Java集合之HashMap源码解析(JDK8)

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景非常丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  4. 集合底层源码分析之HashMap《上》(三)

    集合底层源码分析之HashMap<上>(三) 前言 源码分析 HashMap主要属性及构造方法分析 tableSizeFor()方法源码分析 Node类源码分析 TreeNode类源码分析 ...

  5. 【Java集合源码剖析】HashMap源码剖析

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票,谢 ...

  6. 【JAVA进阶】java中的集合(番外篇3)- HashMap源码底层数据结构分析

    写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多. 痛定思痛的我决定从今天开始系统的梳理下知识架构,记录 ...

  7. HashMap源码实现分析

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 一.前言 HashMap 顾名思义,就是用hash表的原理 ...

  8. HashMap 源码详细分析(JDK1.8)

    1. 概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值 ...

  9. 【Java深入研究】9、HashMap源码解析(jdk 1.8)

    一.HashMap概述 HashMap是常用的Java集合之一,是基于哈希表的Map接口的实现.与HashTable主要区别为不支持同步和允许null作为key和value.由于HashMap不是线程 ...

最新文章

  1. 有人用这个表情包,被腾讯起诉了!
  2. python用于pmc排产可以吗_生产计划员每天都在抓狂,插单、排产问题到底该怎么解决?...
  3. gogs mysql 报错_docker上对gogs二次开发
  4. React开发(213):React在 DevTools 中显示自定义名称
  5. PAT:1047. Student List for Course (25) AC
  6. STM32使用DMA接收串口数据
  7. SpringMVC学习指南【笔记4】数据绑定、表单标签库、转换器、格式化、验证器
  8. 9款最佳iPhone WiFi工具和网络分析工具,附下载链接
  9. touchmove 长按_「jQuery实现移动端长按事件」- 海风纷飞Blog
  10. 计算机excel四舍五入,如何在Excel中设置四舍五入
  11. 深度:老年消费品细分领域潜藏爆品机会,老花镜市场6000万风险投资揭示老年消费升级背后的创新机会!
  12. 哪款分体式蓝牙耳机最好用?分体式蓝牙耳机推荐!
  13. C#/.NET 系统优化专题(搜索引擎Lucene的使用)
  14. 2022年玩具泡泡机市场前景分析及研究报告
  15. [RK3399]电磁屏:优派viewsonic与扩展IO(PCA9534)
  16. 【软件测试】测试理论之性能测试技术
  17. 杰克马,请问有钱就是好人吗?
  18. 判别两棵树是否相等 设计算法_BAIR最新RL算法超越谷歌Dreamer,性能提升2.8倍
  19. 简易版------打砖块
  20. 用Python分析韩国女团喜欢什么单词

热门文章

  1. Word2016设置护眼色
  2. 最新ICCV 2021 | 虚拟试衣(21)图像编辑-文本引导(22)图像编辑-单样本(23)生成对抗GAN...
  3. eMule连接到进ed2k网络分析
  4. 富士施乐m115b怎么连接电脑_施乐 m115b打印机怎么安装
  5. Python教你如何将3个数升序排列
  6. 关于RF框架的一些整理
  7. JAVA文法bnf_关于java:BNF命题逻辑ANTLR的语法
  8. 360加固签名验证_360加固助手加固应用并自动签名的方法
  9. android app在线商城,ECMobile:只需一步即可生成iOS或Android原生APP商城应用
  10. Python正则re模块学习笔记