HashMap

花了一天的时间对hashmap进行了学习总结,如有帮助,请点个赞哦,万分感谢!!!

 

HashTable

  • 核心是基于哈希值的数组链表
  • O(1)的平均查找、插入、删除时间
  • 致命缺陷是哈希值的碰撞

 

HashMap:对Map接口的基于HashTable的实现


HashMap如何决定元素存储位置?

调用对象的hashcode()方法,得到hashcode,再调用hash()方法,得到数组的下标索引bucketIndex ,决定了存储位置

如何存储?

系统总是将新添加的 Entry 对象放入 table 数组的 bucketIndex 索引处——如果 bucketIndex 索引处已经有了一个 Entry 对象,那新添加的 Entry 对象指向原有的 Entry 对象(产生一个 Entry 链),如果 bucketIndex 索引处没有 Entry 对象,则新放入的 Entry 对象指向 null,也就是没有产生 Entry 链。 HashMap里面没有出现hash冲突时,没有形成单链表时,hashmap查找元素很快,get()方法能够直接定位到元素。但是出现单链表后,单个bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。

       通过上面可知如果多个hashCode()的值落到同一个桶内的时候,这些值是存储到一个链表中的。最坏的情况下,所有的key都映射到同一个桶中,这样hashmap就退化成了一个链表——查找时间从O(1)到O(n)。也就是说我们是通过链表的方式来解决这个Hash碰撞问题的

JDK1.7的HashMap(数组+链表

HashMap规定hash桶(数组)的个数 必须是2的n次方个, 默认是16个 (若不为2的幂,源码会向上取整为2的幂)
默认负载因子为0.75(在时间和空间复杂度上一个折中的值)

  • 为什么HashMap的数组大小一定要是2的幂?
    • 对象有了自己对应的hashcode后,需要存储进桶中,那么该进入哪个数组呢?需要得到一个数组下标
      • hashmap采用"按位与运算"方式,[  h & length-1  ] hashcode和数组长度-1进行按位与运算
        • 假设length是2^5,即16,二进制位10000,那么length-1,对应为1111,然后跟hash按位进行与运算,假设hashcode为10010.....1001,进行与运算后得到1001,便得到一个数组下标
  • 总结:因为HashMap通过调用hash方法,把对象的hashcode跟数组长度-1进行 按位与运算,所以只有当数组的长度是2^n的时候,进行-1操作,得到全为1的值,跟hashcode进行按位与运算,便能够非常快速的用位运算的方式拿到数组的下标,并且分布是均匀的。

创建数组的时候并未开辟桶空间,第一次put时才开辟桶空间。

扩容(resize)效率非常低

不停往数组中放元素,当size超过 阈值threshold(阈值=容量×负载因子:16×0.75=12)后,hashtable会扩容一倍,将当前map中的元素重新hash(rehash)迁移(transfer)到新的数组中去

java7中HashMap的问题

1.HashMap是线程不安全的
迁移时存在并发环境中易死锁的问题
迁移时无保存顺序,多线程下可能成环

详细可见:https://blog.csdn.net/gyflyx/article/details/17286973

2.潜在的安全隐患
比如Apache的Tomcat使用hashtable存储http请求的parameter,所以黑客可以恶意设计了一组hashcode相同的参数,迫使hashtable退化成链表,链表的查找操作复杂度是O(n),引发DoS,(大量查询可以吃尽CPU内存)
 

java1.8 HashMap(数组+链表+红黑树

Java8碰撞优化提升

如果某个桶中的记录过大的话(当前是TREEIFY_THRESHOLD = 8),HashMap会动态的使用一个专门的treemap实现来替换掉它。这样做的结果会更好,是O(logn),而不是糟糕的O(n)
它是如何工作的?
        前面产生冲突的那些KEY对应的记录只是简单的追加到一个链表后面,这些记录只能通过遍历来进行查找。但是超过这个阈值后HashMap开始将列表升级成一个二叉树,使用哈希值作为树的分支变量。如果两个哈希值不等,但指向同一个桶的话,较大的那个会插入到右子树里如果哈希值相等,HashMap希望key值最好是实现了Comparable接口的,这样它可以按照顺序来进行插入。这对HashMap的key来说并不是必须的,不过如果实现了当然最好。
        如果没有实现这个接口,在出现严重的哈希碰撞的时候,就别指望能获得性能提升了。
        这个性能提升有什么用处?比方说恶意的程序,如果它知道我们用的是哈希算法,它可能会发送大量的请求,导致产生严重的哈希碰撞。然后不停的访问这些key就能显著的影响服务器的性能,这样就形成了一次拒绝服务攻击(DoS)。JDK 8中从O(n)到O(logn)的飞跃,可以有效地防止类似的攻击,同时也让HashMap性能的可预测性稍微增强了一些。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值