Hashtable、HashMap、TreeMap的区别

是什么?(都是最常见的一些Map实现,是以键值对的方式存储和操作数据的容器类型)

Map从狭义上来说,不属于(Collection)集合类型

Hashtable (同步的)早期提供的一个哈希表实现,不支持null键和值,因为性能原因(全程都加了锁)使用较少,扩展了Dictionary类,类结构上与HashMap之类明显不同。

HashMap  同Hashtable相似,不是同步的,支持null键和值。通常情况下,HashMap进行put、get操作,可以达到常数时间的性能,所以 HashMap 是绝大部分利用键值对存取场景的首选,非常依赖于哈希码的有效性(hashCode和equals的一些基本约定—— equals相等hashCode一定相等/重写了hashCode也要重写equals/hashCode需要保持一致性,状态改变返回的哈希值仍然要一致/equals的对称、反射、传递等特性)

TreeMap 是基于红黑树的一种提供顺序访问的Map,和HashMap不同,它的get、put、remove等操作都是O(log(n))的时间复杂度,整体顺序是由键的顺序关系决定的,通过 Comparator 或 Comparable(自然顺序)来决定。

LinkedHashMap 通常提供的是遍历顺序符合插入顺序,它的实现是通过为键值对维护一个双向链表。通过特定构造函数,我们可以创建反映访问顺序的实例,put、get、compute等都算作“访问”。这种行为适用于一些特定应用场景,例如,我们构建一个空间占用敏感的资源池,希望可以自动将最不常被访问的对象释放掉,这就可以利用 LinkedHashMap 提供的机制来实现)

HashMap 内部结构:可看做是数组和链表结合组成的符合结构,数组被分为一个一个桶(bucket),通过哈希值决定了键值对在这个数组的寻址;哈希值相同的键值对,则以链表形式存储,如果链表大小超过阈值,链表就会被改造为树形结构。 


以下为HashMap put方法源码:

  • 如果表格是 null,resize 方法会负责初始化它,这从 tab = resize() 可以看出。

  • resize 方法兼顾两个职责,创建初始存储表格,或者在容量不满足需求的时候,进行扩容(resize)。

  • 在放置新的键值对的过程中,如果发生下面条件,就会发生扩容。

     进一步分析resize方法:

 不考虑极端情况(容量理论最大极限由 MAXIMUM_CAPACITY 指定,数值为 1<<30,也就是 2 的 30 次方),可归纳为:

  • 门限值等于(负载因子)x(容量),如果构建 HashMap 的时候没有指定它们,那么就是依据相应的默认常量值。

  • 门限通常是以倍数进行调整 (newThr = oldThr << 1),我前面提到,根据 putVal 中的逻辑,当元素个数超过门限大小时,则调整 Map 大小。

  • 扩容后,需要将老的数组中的元素重新放置到新的数组,这是扩容的一个主要开销来源。

        容量负载系数决定了可用的桶的数量,空桶太多会浪费空间,如果使用的太满则会严重影响操作的性能。极端情况下,假设只有一个桶,那么它就退化成了链表,完全不能提供所谓常数时间存的性能。 负载因子 * 容量 > 元素数量,所以:预先设置的容量需要满足,大于“预估元素数量 / 负载因子”,同时它是 2 的幂数。

负载因子:

  • 如果没有特别需求,不要轻易进行更改,因为 JDK 自身的默认负载因子是非常符合通用场景的需求的。

  • 如果确实需要调整,建议不要设置超过 0.75 的数值,因为会显著增加冲突,降低 HashMap 的性能。

  • 如果使用太小的负载因子,按照上面的公式,预设容量值也进行调整,否则可能会导致更加频繁的扩容,增加无谓的开销,本身访问性能也会受影响。

树化改造逻辑:

当 bin 的数量大于 TREEIFY_THRESHOLD 时:

  • 如果容量小于 MIN_TREEIFY_CAPACITY,只会进行简单的扩容。

  • 如果容量大于 MIN_TREEIFY_CAPACITY ,则会进行树化改造。

为什么HashMap要树化?本质上是个安全问题

因为在元素放置过程中,如果一个对象哈希冲突,都被放置到同一个桶里,则会形成一个链表,我们知道链表查询是线性的,会严重影响存取的性能。在现实世界,构造哈希冲突的数据并不是非常复杂的事情,恶意代码就可以利用这些数据大量与服务器端交互,导致服务器端 CPU 大量占用,这就构成了哈希碰撞拒绝服务攻击

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laughing_Xie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值