图解HashMap

HashMap在JDK1.7中多线程并发会出现死循环,超详细图解 就是JDK1.7的头插法造成了循环问题,因此在JDK1.8中对HashMap的扩容方法进行了改进,从头插法改成了尾插法。 阅读详情

概念:

其实HashMap也是用到这种思路,HashMap作为一种数据结构,像数组和链表一样用于常规的增删改查,在存数据的时候(put)并不是随便乱放,而是会先做一次类似“分类”的操作再存储,一旦“分类”存储之后,下次取(get)的时候就可以大大缩短查找的时间。我们知道数组在执行查、改的效率很高,而增、删(不是尾部)的效率低,链表相反,HashMap则是把这两者结合起来,看下HashMap的数据结构

从上面的结构可以看出,通常情况下HashMap是以数组和链表的组合构成(Java8中将链表长度超过8的链表转化成红黑树)。结合上面找手机的例子,我们简单分析下HashMap存取操作的心路历程。put存一个键值对的时候(比如存上图盖伦),先根据键值”分类”,”分类”一顿操作后告诉我们,盖伦应该属于14号坑,直接定位到14号坑。接下来有几种情况:

  • 14号坑没人,nice,直接存值;
  • 14号有人,也叫盖伦,替换原来的攻击值;
  • 14号有人,叫老王!插队到老王前面去(单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置)

get取的时候也需要传键值,根据传的键值来确定要找的是哪个”类别”,比如找火男,”分类”一顿操作够告诉我们火男属于2号坑,于是我们直接定位到2号坑开始找,亚索不是…找到火男。

小结

HashMap是由数组和链表组合构成的数据结构,Java8中链表长度超过8时会把长度超过8的链表转化成红黑树;存取时都会根据键值计算出”类别”(hashCode),再根据”类别”定位到数组中的位置并执行操作。

HashCode是什么

还是举个栗子:一个工厂有500号人,下图用两种方案来存储厂里员工的信件。

左右各有27个信箱,左边保安大哥存信的时候不做处理,想放哪个信箱就放哪个信箱,当员工去找信的时候,只好挨个信箱找,再挨个比对信箱里信封的名字,万一哥们脸黑,要找的放在最后一个信箱的最底下,悲剧…所以这种情况的时间复杂度为O(N);右边采用HashCode的方式将27个信箱分类,分类的规则是名字首字母(第一个箱子放不写名字的哥们),保安大哥将符合对应姓名的信件放在对应的信箱里,这样员工就不用挨个找了,只需要比对一个信箱里的信件即可,大大提高了效率,这种情况的时间复杂度趋于一个常数O(1)。

例子中右图其实就是hashCode的一个实现,每个员工都有自己的hashCode,比如李四的hashCode是L,王五的hashCode是W(这取决于你的hash算法怎么写),然后我们根据确定的hashCode值把信箱分类,hashCode匹配则存在对应信箱。在Java的Object中可以调用hashCode()方法获取对象hashCode,返回一个int值。那么会出现两个对象的hashCode一样吗?答案是会的,就像上上个例子中盖伦和老王的hashCode就一样,这种情况网上有人称之为”hash碰撞”,出现这种所谓”碰撞”的处理上面已经介绍了解决思路,具体源码后续介绍。

小结

hashCode是一个对象的标识,Java中对象的hashCode是一个int类型值。通过hashCode来指定数组的索引可以快速定位到要找的对象在数组中的位置,之后再遍历链表找到对应值,理想情况下时间复杂度为O(1),并且不同对象可以拥有相同的hashCode。

HashMap的时间复杂度

通过上面信箱找信的例子来讨论下HashMap的时间复杂度,在使用hashCode之后可以直接定位到一个箱子,时间的耗费主要是在遍历链表上,理想的情况下(hash算法写得很完美),链表只有一个节点,就是我们要的

那么此时的时间复杂度为O(1),那不理想的情况下(hash算法写得很糟糕),比如上面信箱的例子,假设hash算法计算每个员工都返回同样的hashCode

所有的信都放在一个箱子里,此时要找信就要依次遍历C信箱里的信,时间复杂度不再是O(1),而是O(N),因此HashMap的时间复杂度取决于算法的实现上,当然HashMap内部的机制并不像信箱这么简单,在HashMap内部会涉及到扩容、Java8中会将长度超过8的链表转化成红黑树,这些都在后续介绍。

小结

HashMap的时间复杂度取决于hash算法,优秀的hash算法可以让时间复杂度趋于常数O(1),糟糕的hash算法可以让时间复杂度趋于O(N)。

负载因子

我们知道HashMap中数组长度是16(什么?你说不知道,看下源码你就知道),假设我们用的是最优秀的hash算法,即保证我每次往HashMap里存键值对的时候,都不会重复,当hashmap里有16个键值对的时候,要找到指定的某一个,只需要1次;

之后继续往里面存值,必然会发生所谓的”hash碰撞”形成链表,当hashmap里有32个键值对时,找到指定的某一个最坏情况要2次;当hashmap里有128个键值对时,找到指定的某一个最坏情况要8次

随着hashmap里的键值对越来越多,在数组数量不变的情况下,查找的效率会越来越低。那怎么解决这个问题呢?只要增加数组的数量就行了,键值对超过16,相应的就要把数组的数量增加(HashMap内部是原来的数组长度乘以2),这就是网上所谓的扩容,就算你有128个键值对,我们准备了128个坑,还是能保证”一个萝卜一个坑”。

其实扩容并没有那么风光,就像ArrayList一样,扩容是件很麻烦的事情,要创建一个新的数组,然后把原来数组里的键值对”放”到新的数组里,这里的”放”不像ArrayList那样用原来的index,而是根据新表的长度重新计算hashCode,来保证在新表的位置,老麻烦了,所以同一个键值对在旧数组里的索引和新数组中的索引通常是不一致的(火男:”我以前是3号,怎么现在成了127号,给我个完美的解释!”新表:”大清亡了,现在你得听我的”)。另外,我们也可以看出这是典型的以空间换时间的操作。

说了这么多,那负载因子是个什么东西?负载因子其实就是规定什么时候扩容。上面我们说默认hashmap数组大小为16,存的键值对数量超过16则进行扩容,好像没什么毛病。然而HashMap中并不是等数组满了(达到16)才扩容,它会存在一个阀值(threshold),只要hashmap里的键值对大于等于这个阀值,那么就要进行扩容。阀值的计算公式:

阀值 = 当前数组长度✖负载因子

hashmap中默认负载因子为0.75,默认情况下第一次扩容判断阀值是16 ✖ 0.75 = 12;所以第一次存键值对的时候,在存到第13个键值对时就需要扩容了;或者另外一种理解思路:假设当前存到第12个键值对:12 / 16 = 0.75,13 / 16 = 0.8125(大于0.75需要扩容) 。肯定会有人有疑问,我要这铁棒有何用?不,我要这负载因子有何用?直接规定超过数组长度再扩容不就行了,还省得每次扩容之后还要重新计算新的阀值,Google说取0.75是一个比较好的权衡,当然我们可以自己修改,HashMap初识化时可以指定数组大小和负载因子,你完全可以改成1。

public HashMap(int initialCapacity, float loadFactor)

我的理解是这负载因子就像人的饭量,有的人吃要7分饱,有的人要10分饱,稳妥起见默认让我们7.5分饱。

小结

在数组大小不变的情况下,存放键值对越多,查找的时间效率会降低,扩容可以解决该问题,而负载因子决定了什么时候扩容,负载因子是已存键值对的数量和总的数组长度的比值。默认情况下负载因子为0.75,我们可在初始化HashMap的时候自己修改。

hash与Rehash

hash和rehash的概念其实上面已经分析过了,每次扩容后,转移旧表键值对到新表之前都要重新rehash,计算键值对在新表的索引。如下图火男这个键值对被存进hashmap到后面扩容,会经过hash和rehash的过程

第一次hash可以理解成’”分类”‘,方便后续取、改等操作可以快速定位到具体的”坑”。那么为什么要进行rehash,按照之前元素在数组中的索引直接赋值,例如火男之前3号坑,现在跑到30号坑。

个人理解是,在未扩容前,可以看到如13号链的长度是3,为了保证我们每次查找的时间复杂度O趋于O(1),理想的情况是”一个萝卜一个坑”,那么现在”坑”多了,原来”3个萝卜一个坑”的情况现在就能有效的避免了。

源码分析

Java7源码分析

先看下Java7里的HashMap实现,有了上面的分析,现在在源码中找具体的实现。

//HashMap里的数组
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//Entry对象,存key、value、hash值以及下一个节点
static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    int hash;
}
//默认数组大小,二进制1左移4位为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//负载因子默认值
static final float DEFAULT_LOAD_FACTOR = 0.75f; 
//当前存的键值对数量
transient int size; 
//阀值 = 数组大小 * 负载因子
int threshold;
//负载因子变量
final float loadFactor;
//默认new HashMap数组大小16,负载因子0.75
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//可以指定数组大小和负载因子
public HashMap(int initialCapacity, float loadFactor) {
    //省略一些逻辑判断
    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    //空方法
    init();
}

以上就是HashMap的一些先决条件,接着看平时put操作的代码实现,put的时候会遇到3种情况上面已分析过,看下Java7代码:

public V put(K key, V value) {
        //数组为空时创建数组
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //key为空单独对待
        if (key == null)
            return putForNullKey(value);
        //①根据key计算hash值
        int hash = hash(key);
        //②根据hash值和当前数组的长度计算在数组中的索引
        int i = indexFor(hash, table.length);
        //遍历整条链表
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //③情况1.hash值和key值都相同的情况,替换之前的值
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                //返回被替换的值
                return oldValue;
            }
        }
        modCount++;
        //③情况2.坑位没人,直接存值或发生hash碰撞都走这
        addEntry(hash, key, value, i);
        return null;
    }

先看上面key为空的情况(上面画图的时候总要在第一格留个空key的键值对),执行 putForNullKey() 方法单独处理,会把该键值对放在index0,所以HashMap中是允许key为空的情况。再看下主流程:

步骤①.根据键值算出hash值 — > hash(key)

步骤②.根据hash值和当前数组的长度计算在数组中的索引 — > indexFor(hash, table.length)

static int indexFor(int h, int length) {
    //hash值和数组长度-1按位与操作,听着费劲?其实相当于h%length;取余数(取模运算)
    //如:h = 17,length = 16;那么算出就是1
    //&运算的效率比%要高
    return h & (length-1);
}

步骤③情况1.hash值和key值都相同,替换原来的值,并将被替换的值返回。

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    V oldValue = e.value;
    e.value = value;
    e.recordAccess(this);
    //返回被替换的值
    return oldValue;
}

步骤③情况2.坑位没人或发生hash碰撞 — > addEntry(hash, key, value, i)

void addEntry(int hash, K key, V value, int bucketIndex) {
    //当前hashmap中的键值对数量超过阀值
    if ((size >= threshold) && (null != table[bucketIndex])) {
        //扩容为原来的2倍
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        //计算在新表中的索引
        bucketIndex = indexFor(hash, table.length);
    }
    //创建节点
    createEntry(hash, key, value, bucketIndex);
}

如果put的时候超过阀值,会调用 resize() 方法将数组大小扩大为原来的2倍,并且根据新表的长度计算在新表中的索引(如之前17%16 =1,现在17%32=17),看下resize方法

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
    table = newTable;
    //步骤④修改阀值
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

上面的重点是步骤②,看下它具体的转移操作

void transfer(Entry[] newTable, boolean rehash) {
    //获取新数组的长度
    int newCapacity = newTable.length;
    //遍历旧数组中的键值对
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> 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;
        }
    }
}

这段for循环的遍历会使得转移前后键值对的顺序颠倒(Java7和Java8的区别),画个图就清楚了,假设石头的key值为5,盖伦的key值为37,这样扩容前后两者还是在5号坑。第一次:

第二次

最后再看下创建节点的方法

void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

创建节点时,如果找到的这个坑里面没有存值,那么直接把值存进去就行了,然后size++;如果是碰撞的情况,

前面说的以单链表头插入的方式就是这样(盖伦:”老王已被我一脚踢开!“),总结一下Java7 put流程图

相比put,get操作就没这么多套路,只需要根据key值计算hash值,和数组长度取模,然后就可以找到在数组中的位置(key为空同样单独操作),接着就是遍历链表,源码很少就不分析了。

Gitlab+Jenkins+Docker+Harbor+K8s+Rancher集群搭建CICD平台 本文以诺依项目为例详细记录了Kubernetes集群的部署过程以及配套DevOps环境的搭建。主要内容包括:1) 配置8个节点的软硬件环境;2) 基础环境准备(网络、防火墙、Docker等);3) 三节点Kubernetes集群部署;4) GitLab代码仓库安装;5) Harbor私有镜像仓库搭建;6) Jenkins持续集成工具配置;7) 基于若依项目的CI/CD实践;8) Rancher集群管理工具部署。 阅读详情

相关推荐

大麦网抢票软件工具开发系列(一)

前言《大麦网》都知道吧,就是那个被网友声称“大麦网你卖啥了?”票务平台。近期有听到朋友说《大麦网》的票很难抢,基本上都是说:“哎哟喂,被挤爆啦,请稍后重试!”,也不知道《大麦网》...

架构文摘 1万+

图文并茂,HashMap经典详解!

什么是HashMap?HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不...

Java笔记虾 1484

Meta DINOv3实战指南:如何用7B参数ViT模型提升你的视觉任务性能(附Hugging Face部署教程)

本文提供了Meta DINOv3 7B参数ViT模型的实战部署指南。文章详细解析了该自监督视觉模型的优势,并重点演示了如何在Hugging Face平台上高效加载这一大规模模型,同时给出了图像分类、语义分割等下游任务的性能提升技巧与代码示例,帮助开发者在实际视觉任务中应用这一前沿技术。

pz89012345的博客 323

图说HashMap

HashMap数据结构 HashMap.putHashMap.addEntryHashMap.resize

猩猩之火可以燎原 733

Cadence 17.2配置CIS数据库报错?手把手解决ODBC驱动位数不匹配问题

本文详细解析了Cadence 17.2配置CIS数据库时常见的ODBC驱动位数不匹配问题,提供了从系统环境检查到分步解决方案的完整指南。重点解决32位应用程序与64位ODBC驱动之间的架构冲突,包含驱动安装、DSN配置及高级排查技巧,帮助电子工程师快速恢复设计环境。

play7的博客 970

45个经典Linux面试题(非常详细)零基础入门到精通,收藏这一篇就够了

(文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如 i 节点,磁盘分布图,间接块,超级块等。$ chmod 751 file 给 file 的属主分配读、写、执行(7)的权限,给 file 的所在组分配读、执行(5)的权限,给其他用户分配执行(1)的权限。linux命令’hash’管理着一个内置的哈希表,记录了已执行过的命令的完整路径, 用该命令可以打印出你所使用过的命令以及执行的次数。创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件。

小陈的博客 2069

关于Map数据类型的理解

1、什么是map数据类型? map是K-V类型的数据,map是一个该种数据类型的顶级接口。 2、map的实现类和组成以及扩充规则 map有多种实现类,其中日常开发中主要使用的有hashmap,linkedhashmap,treemap等。map实际就是是由一个一个entry对象组成,entry对象中包含key、value、next以及hash值。其中key和value就是我们设置的key和value,next是指向下一个entry对象,hash值是根据key的值经过一系......

cyy13579的博客 1万+

300m半径无人机单兵侦测设备技术详解

300m半径无人机单兵侦测设备凭借其卓越的侦测性能、高效的识别能力、便捷的告警方式以及广泛的应用场景,成为现代军事、安保及应急救援领域的重要装备。300m半径无人机单兵侦测设备,融合了最新的无人机技术、高精度传感技术与实时数据处理技术,为单兵作战、巡逻及侦察任务提供了前所未有的视野与效率。该设备通过无人机搭载的高性能侦测模块,能够在复杂环境中实现300米范围内的精准侦测,大幅提升单兵的战场感知能力和决策效率。设备为被动式探测设备,确保安静安全运行,不暴露目标 设备耗电功率小,可长时间在野外使用。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1118

HashMap的工作原理(图文+例子)详解,绝对简单通俗易懂

什么是HashMap? 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与Hashtable大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比...

Huang405267467的博客 1万+

【系统架构设计师】案例分析考点情况分析和解题技巧(包括2009~2024年考点详情)

复习方法建议仍然是以真题为主,只不过和综合题不同,标准答案并不会涵盖所有可能的考点。案例的考察方法和论文不太一样,它考察两大块的内容:

数据知道的博客 1万+

C#上位机之海康相机开发(SDK)实战教程

SDK的接口设计遵循“句柄-操作-释放”模式,掌握→→→的流程,就能完成基础开发;工业场景需重点关注稳定性:断连重连、内存管理、抗干扰(网口相机的IP设置、触发信号的可靠性);进阶功能(如图像处理、多相机同步)需结合视觉库(EmguCV)和硬件特性(GPIO触发),但前提是保证基础采集的稳定。建议新手先跑通SDK自带的C#示例,理解每个函数的作用,再逐步替换为自己的代码。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 2191

都说HashMap是判断Java程序员基础是否扎实的标准之一,你怎么看?

前言 HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 HashMap的数据结构 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 文字描述永远要配上图才能更好的讲解数据结构,HashMap的结..

weixin_49723683的博客 565

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

图解hashmap的put方法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

百味皆苦的博客 2万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

HashMap图解

HashMap的数据结构和put、get、resize等操作的图解,看图轻松掌握HashMap (目前还不包括红黑树相关的部分) HashMap数据结构如下图 HashMap之put操作如下图 HashMap之get操作如下图 HashMap之resize操作如下图 转载于:https://www.cnblogs.com/oldbai/p/9846955.html...

weixin_30337251的博客 235

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

带你用图解源码的形式,了解HashMap的扩容!!!

系列文章目录 文章目录系列文章目录前言一、pandas是什么?二、使用步骤1.引入库 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例可供参考 一、pandas是什么? Map<Integer,Integer> map=new HashMap<>(8); map.put(0,1);

烟花易冷 452

HashMap的循环链表图解

HashMap的循环链表图解 @author:Jingdai @date:2021.03.20 复习HashMap的知识点,总是看到jdk1.7前在多线程操作时可能会出现循环链表问题,不是很理解,于是研究源码并画图终于搞懂,记录一下。 由于本人电脑只有jdk1.5和jdk1.8,所以以下分析均基于jdk1.5,jdk1.7应该一样。 循环链表发生的地方 循环链表发生在多个线程同时对HashMap进行put操作使HashMap 扩容的时候。下面看代码。 public V put(K key, V valu

qq_41512783的博客 5806

【集合框架】HashMap图解

QiuBika_061的博客 204

阅读 JDK 源码:HashMap 扩容总结及图解

本文基于 Java8,通过阅读 HashMap 的 resize 方法了解其扩容原理,并对桶上链表的迁移过程进行调试,画图以加深理解。 文章目录1. 扩容的时机2. 扩容的源码如果是链结构如果是树结构3. 链表迁移算法执行结果执行过程图示4. 总结 1. 扩容的时机 HashMap 中 put 入第一个元素,初始化数组 table。 HashMap 中的元素数量大于阈值 threshold。 threshold = capacity * load factor。 当 size > thresho

Sumkor的博客 8229

【面试】Java集合篇--图解HashMap的put方法的具体流程

学亮编程手记 1575

图解HashMap(二)

点击上方蓝字关注公众号 码个蛋第226次推文 图解HashMap多线程死循环 作者:HuYounger 博客:http://rkhcy.github.io/ 文章目录 前言 概述 Java7分析 Java8分析 总结 0 前言 上次的图解Hash

山有多高的博客 645

JAVA jdk1.8 HashMap 存值流程图解

JCccc

默默不代表沉默 1680

图解HashMap源码

1. 基础变量 这些变量可能会在源码阅读中反复出现 capacity: 桶的数量 也就是table长度 threshold:默认16 capacity*loadFactor 当size大于threshold的时候会进行resize loadFactor:加载因子 默认 0.75 减少hash冲突 modCount:数据修改次数 Iterator初始化的时候(HashIterator构造函数)e...

记录 3094

图解HashMap(一)

hashmap

QG 2758

带你走进Java集合-HashMap源码-put方法图解实战

这一篇文章我利用图解的方法来进行分析。首先看一下网上一个比较好的流程图: 带你走进Java集合-HashMap源码-put方法图解实战 实例:按照正常的流程走一下put方法 第一行代码:Map<String,Integer>map = new HashMap<>(); 第二行代码:map.put(null,10); 第三行代码:map.put(“dd”,null); 第四...

AD_plus的博客 1098

hashmap底层储存结构图解

publicclassHashMap<K,V>extends<K,V>implementsMap<K,V>,Cloneable{}HashMap是以key-value形式存储数据的。:继承了,大大减少了实现Map接口时需要的工作量。:实现了Map,提供了所有可选的Map操作。:表明其可以调用clone()方法来返回实例的拷贝。:表明该类是可以序列化的。1.3 put()数据原理分析图解。

风一样 1028
上一篇: ViewPager(分发+无限滑动)
下一篇: 依赖注入和Dagger2
missingcall
博客等级 码龄10年 8粉丝 40原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值