首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

个人对哈希数据结构学习总结 -- 实践篇 -- 上

null; } 既然ConcurrentHashMap的get方法没有加锁,那么多线程情况下又是如何确保数据读取一致性的呢?...ConcurrentHashMap中对共享数据结构或者共享变量很多读操作都没有加锁: get方法没有加锁 sumCount累加计数方法中读取计数器的过程中也没有加锁 final long sumCount...,那么如果线性探测的过程中遇到了被回收的空entry时,此时又该做什么呢?...---- put 上面解析get方法实现时也说到了ThreadLocalMap采用的是线性探测法解决的哈希冲突,put操作的整体流程也是先看目标位置有无人占着,如果有就继续往后找,直到找到一个NULL桶或者空桶...cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } 当put方法发生哈希碰撞后,在执行线性探测的过程中如果遇到了空桶

28420

为什么要重写 hashCode 和 equals 方法?

面试官狡猾的笑了,说是你既然没有重写过 hashCode 方法,你怎么把自定义对象放进去的? 我勒个去,原来你在这等着我呢,没想到这还是个连环炮,惹不起惹不起,认怂三连 ?...所以哈希查找的第二个步骤就是处理冲突 处理哈希碰撞冲突:有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法和线性探测法。...按照形成探查序列的方法不同,可将开放定址法区分为线性探查法、线性补偿探测法以及随机探测等。限于篇幅,我们此处只讨论线性探查法。...按照线性探测法处理冲突,如果生成哈希地址的连续序列愈长 ( 即不同关键字值的哈希地址相邻在一起愈长 ) ,则当新的记录加入该表时,与这个序列发生冲突的可能性愈大。...但运行结果还是会出乎我们意料: map.get(k2) : null 明明 103号位置已经有 k1,但打印输出结果依然是 null。 ?

52120
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    大话 ThreadLocal

    通常,该方法只会被调用一次,但如果在后续的操作中,在调用了remove方法后调用get方法,那么该方法(initialValue)将会被调用多次(因为,此时线程的局部变量需要重新被初始化)。...基于这种策略的所有方法被统称为“开放地址”哈希表 线性探测法(“开放地址”哈希表的一种实现方式) 开放地址哈希表中最简单的方法叫做“线性探测”法:当碰撞发生时(当一个键的Hash值已经被另一个不同的键占用...删除操作 如何从基于线性探测的哈希表中删除一个键?仔细想一想,你会发现直接将该键所在的位置设为null是不行的,因为这会使得在此位置之后的元素无法被查找。...命题 M :在一张大小为 M 并含有 N = α * M 个键的基于线性探测的哈希表中,基于假设 J ,命中和未命中的查找所需的探测次数分别为: ?...= entry && null == entry.get() 的含义 null == entry:表示给位置没有对象 null !

    74340

    自信,这是最好的ThreadLocal分析

    但是在某些情况下,我们需要线程拥有自己独立的数据(比如 Looper ),与别的线程隔离开来,该如何做?...ThreadLocal 方法详解 ThreadLocal 中核心的就是set,get 方法,分别来看下实现。...可以快速定位,可以 // 直接从h位置上拿到值,当然,前提是第一次计算出来的h位置是空闲的,没有经过线性探测过,如果经过线性探测了,这个最终这个h也不是当前元素本该存放的位置...这是在不扫描与全部扫描两个方案中做了均衡,就来个二分扫描吧。 那这个方法跟上面expungeStaleEntry有啥区别呢?...说下我的见解: // 首先,这个方法在set时调用,走到这个方法,说明已经发生了碰撞,并且遇到了key失效的位置,那么基于线性探测法, // 需要往后面查找能插入的位置

    51720

    面试官:小伙子,听说你看过ThreadLocal源码?(万字图文深度解析ThreadLocal)

    ThreadLocalMap中过期key的清理机制?探测式清理和启发式清理流程? ThreadLocalMap.set()方法实现原理? ThreadLocalMap.get()方法实现原理?...遍历散列数组,线性往后查找,如果找到Entry为null的槽位,则将数据放入该槽位中,或者往后遍历过程中,遇到了key值相等的数据,直接更新即可。...Entry 往后清理,遇到值为null则结束清理,属于线性探测清理。...数据:父类数据:inheritableThreadLocal 实现原理是子线程是通过在父线程中通过调用new Thread()方法来创建子线程,Thread#init方法在Thread的构造方法中被调用...ThreadLocal中,在调用服务B的时候,将traceId写入到请求的Header中,服务B在接收请求时会先判断请求的Header中是否有traceId,如果存在则写入自己线程的ThreadLocal

    1.4K21

    算法原理系列:散列表

    第二,映射函数是为了寻找键与数组下标的关系,使得查找转换成在该数组范围内的索引[0,M-1],可分配的数组大小为M。 ? 存在两个问题,映射函数怎么找,以及对应的键求得的映射值相同时,该如何处理。...所以,当我们定义一个key类时,我们只需要重写它的hashCode()方法即可。 冲突检测拉链法 冲突检测常用的手段有两种,一种是拉链法,一种是线性探测法。...冲突检测线性探测法 开放地址散列表中最简单的方法叫做线性探测法:当碰撞发生时(当一个键的散列值已经被另一个不同的键占用),我们直接检查散列表中的下一个位置(将索引值加1)。...拉链法和线性探测法的详细比较取决去实现的细节和用例对空间和时间的要求。即时基于性能考虑,选择拉链法而非线性探测法也不一定是合理的。...在实践中,两种方法的性能差别主要是因为拉链法为每个键值对都分配了一小块内存而线性探测法则为整张表使用了两个很大的数组。对于非常大的散列表,这些做法对内存管理系统的要求也很不相同。

    48640

    ThreadLocal详解

    ,这些特有变量都被存到了map中,当我们去取特有变量的时候,需要告诉线程要取哪个特有变量,如何分辨这些特有变量呢?...如果此线程还没有线程特有对象,或者线程特有对象中没有我们查找的这个ThreadLocal对象,那么我们就需要执行初始化方法,就是是代码的最后一句 setInitialValue() 我们进入这个方法 private...第一句调用的这个方法InitialValue有没有很熟悉,这就是我们文章开始重写的那个方法(如果不重写,这个方法直接返回null)。...如果位置i没有元素的话,直接新建entry,并且检查是否需要扩容。如果位置i已经有元素,则判断这个entry的key与要插入的是否相等,相等则覆盖;如果为Null,则进行替换。...也就是说这里使用了线性探测的方式来解决hash冲突,为什么使用线性探测呢?毕竟总有人觉得跟链表法比起来,尤其是跟使用了红黑树进行优化的Hashmap中的链表法比起来,线性探测很可能出现连续多次的冲突。

    42030

    深入详解ThreadLocal

    大家好,我是 BookSea。 在我们日常的并发编程中,有一种神奇的机制在静悄悄地为我们解决着各种看似棘手的问题,它就是「ThreadLocal」。...透过本文,我们将揭开它神秘的面纱,并深入理解它是如何优雅处理线程级别的数据隔离,以及在实际开发中如何有效地利用它。 话不多说,我们进入正题。...但其实在 ThreadLocalMap 的实现中以及考虑到这种情况,因此在调用 set()、get()、remove() 方法时,也会清理 key 为 null 的记录。...探测式清理(源码中:expungeStaleEntry() 方法 ) 启发式清理(源码中:cleanSomeSlots() 方法 ) 探测式清理 这种清理方法基于一个事实:在查找特定键时,如果遇到无效条目...get() 方法中遇到key过期的时候会触发一次探测式清理流程。 启发式清理流程中遇到key=null的情况也会触发一次探测式清理流程。 最后,给本篇文章做个总结。

    50520

    深入详解ThreadLocal

    透过本文,我们将揭开它神秘的面纱,并深入理解它是如何优雅处理线程级别的数据隔离,以及在实际开发中如何有效地利用它。话不多说,我们进入正题。...如果当前线程没有该ThreadLocal的值,则调用「initialValue函数」获取初始值返回,所以一般我们使用时需要继承该函数,给出初始值(不重写的话默认返回Null)。...但其实在 ThreadLocalMap 的实现中以及考虑到这种情况,因此在调用 set()、get()、remove() 方法时,也会清理 key 为 null 的记录。为什么使用弱引用而不是强引用?...探测式清理(源码中:expungeStaleEntry() 方法 )启发式清理(源码中:cleanSomeSlots() 方法 )探测式清理这种清理方法基于一个事实:在查找特定键时,如果遇到无效条目(即键为...图片get() 方法中遇到key过期的时候会触发一次探测式清理流程。图片启发式清理流程中遇到key=null的情况也会触发一次探测式清理流程。图片最后,给本篇文章做个总结。

    34540

    java中的reference(四): WeakReference的应用--ThreadLocal源码分析

    那么出现之后该如何处理呢? 参考前文: 解决哈希冲突的常用方法分析 ThreadLocalMap使用了开放定址法,即从发生冲突的那个单元起,按照一定的次序,从哈希表中找到一个空闲的单元。...因此ThreadLocalMap会在每次set和get线性探测的过程中,将key为null的entry进行擦除。...该方法的逻辑是,需要替换的这个位置,通过线性探测查找其上一个位置,一直找到起始位置进行记录,,之后再从这个位置向后探测。探测分为两种情况。如果遇到key相等的Entry,则直接替换value。...cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } } 在set方法中如果clean没有回收长度...通过每次加0x61c88647之后取模能尽量均匀的分布在哈希表中。 ThreadLocalMap 对于hash冲突采用开放定址法中的线性探测法。每次向后加1。

    84700

    ThreadLocal到底有没有内存泄漏?

    寻址 Entry 的 key 是 ThreadLocal 类型的,它是如何在数组中散列的呢?...该操作其实就是处理哈希冲突的「线性探测」方法:当某个位置已被占用,向后探测下一个位置。 若 staleSlot 前面存在过期的 Entry,则执行清理操作。...}     }     return i; } 该方法主要做了哪些工作呢?...由于该方法是在 set 方法内部被调用的,也就是新增/更新时: 如果不扫描和清理,set 方法执行速度很快,但是会存在一些垃圾(过期的 Entry); 如果每次都扫描清理,不会存在垃圾,但是插入性能会降低到...计算 Entry 的位置后 若该槽为空,直接放到这里;并清理一些过期的 Entry,必要时进行扩容。 当遇到散列冲突时,线性探测向后查找数组中为空的、或者已经过期的槽,用新值替换。

    1.1K10

    ThreadLocal全解析——你想要的这里都有

    也就是说一个Entry要么在它的hash位置上,要么就在该位置往后的某一位置上。 由于线性探测发 table 数组中的情况一定是一段一段连续的片段,我们将一个连续的片段称为 run。...比如说把身份信息埋到ThreadLocal中,然后该请求的所有接口都可以获取到这个身份信息。 父子线程传递实现方案 如果子线程想要拿到父线程的中的ThreadLocal值怎么办呢?...由于ThreadLocal的实现机制,在子线程中get时,我们拿到的Thread对象是当前子线程对象,那么他的ThreadLocalMap是null的,所以我们得到的value也是null。...InheritableThreadLocal 那其实很多时候我们是有子线程获得父线程ThreadLocal的需求的,要如何解决这个问题呢?...; InheritableThreadLocal是如何实现在子线程中能拿到当前父线程中的值的呢?

    47211

    算法和数据结构: 十一 哈希表

    有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法和线性探测法。 哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。...在实际中,我们的键并不都是数字,有可能是字符串,还有可能是几个值的组合等,所以我们需要实现自己的哈希函数。 1. 正整数 获取正整数哈希值最常用的方法是使用除留余数法。...线性探测法 线性探测法是开放寻址法解决哈希冲突的一种方法,基本原理为,使用大小为M的数组来保存N个键值对,其中M>N,我们需要使用数组中的空位解决碰撞冲突。如下图所示: ?...第二步是,如果出现哈希值冲突,如何解决,前面介绍了拉链法和线性探测法下面就这两种方法进行讨论: 对于拉链法,查找的效率在于链表的长度,一般的我们应该保证长度在M/8~M/2之间,如果链表的长度大于M/2...如果长度在0~M/8时,我们可以缩小链表。 对于线性探测法,也是如此,但是动态调整数组的大小需要对所有的值从新进行重新散列并插入新的表中。

    98720

    趣味算法:JS实现红绳算法(匹配合适的另一半)

    问题来了:如果没有下标的那一项,当然是undefined,但是如果key值计算后得到的hash值重复了,那怎么办?会被覆盖掉。...我们不允许出现这个问题.因为我们要把所有人的信息都存进去,今天介绍两种方法: 分离链接 线性探测 ? (一)线性探测法 线性探测法是最简单的处理冲突的方法。...(1)插入元素:插入元素时,如果发生冲突,算法将从该槽位向后遍历哈希表,直到找到表中的下一个空槽,并将该值放入到空槽当中。...(2)查找元素:查找元素时,首先散列值所指向的槽,如果没有找到匹配,则继续从该槽向后遍历哈希表,直到:1)找到相应的元素;2)找到一个空槽(指示查找的元素不存在);3)整个哈希表都遍历完毕(指示该元素不存在并且哈希表已满...目前我们的hashTable数据长这样 每个hash即数组下标对应一个链表(如果有)/undefined(如果没有) 中奖规则设计 今天是七夕,于是我取出每个hash对应链表的第7个位置人出来匹配

    70620

    ThreadLocal到底有没有内存泄漏?从源码角度来剖析一波

    ThreadLocal 在某些情况可能产生的「内存泄漏」就跟这个「弱引用」有关,后面再展开分析。 寻址 Entry 的 key 是 ThreadLocal 类型的,它是如何在数组中散列的呢?...该操作其实就是处理哈希冲突的「线性探测」方法:当某个位置已被占用,向后探测下一个位置。 若 staleSlot 前面存在过期的 Entry,则执行清理操作。...由于该方法是在 set 方法内部被调用的,也就是新增/更新时: 如果不扫描和清理,set 方法执行速度很快,但是会存在一些垃圾(过期的 Entry); 如果每次都扫描清理,不会存在垃圾,但是插入性能会降低到...计算 Entry 的位置后 若该槽为空,直接放到这里;并清理一些过期的 Entry,必要时进行扩容。 当遇到散列冲突时,线性探测向后查找数组中为空的、或者已经过期的槽,用新值替换。...此时,如果没有任何 remove 或者 get 等清理 Entry 数组的动作,那么该 Entry 的 value 持有的 Object 就不会被回收掉。这样就产生了内存泄漏。

    74920

    【数据结构与算法】详解什么是哈希表,并用代码手动实现一个哈希表

    但是呢,数组也是有一定的缺点的,如果我们不知道某个元素的下标值,而只是知道该元素在数组中,这时我们想要获取该元素就只能对数组进行线性查找,即从头开始遍历,这样的效率是非常低的,如果一个长度为10000的数组...但这种方法有一个缺点,那就是当数组中连续很长的一个片段都已经插入了数据,此时用线性探测就显得效率没那么高了,因为每次探测的步长都为1,所以这段都已经插入了数据的片段都得进行探测一次,这种现象叫做 聚集。...五、哈希表的方法 老规矩,我们在封装哈希表之前,先来看看哈希表常见的方法都有哪些 方法 含义 put() 向哈希表中插入数据或修改哈希表中数据 get() 获取哈希表中的某个数据 del() 删除哈希表中某个数据...(4)实现get()方法 get()方法是用于查询哈希表中某个数据。...因为我们要实现哈希表的自动扩容与减容,所以在每次容量改变的时候,需要判断新的容量是否为质数,以此来保证之后哈希表中的数据均匀地分布,所以我们还是有必要来封装一下这个方法的。

    2.6K30
    领券