以下基于 JDK1.7 分析。

如图所示,HashMap 底层是基于数组和链表实现的。其中有两个重要的参数:

  • 容量
  • 负载因子

容量的默认大小是 16,负载因子是 0.75,当 HashMapsize > 16*0.75 时就会发生扩容(容量和负载因子都可以自由调整)。

put 方法

首先会将传入的 Key 做 hash 运算计算出 hashcode,然后根据数组长度取模计算出在数组中的 index 下标。

由于在计算中位运算比取模运算效率高的多,所以 HashMap 规定数组的长度为 2^n 。这样用 2^n - 1 做位运算与取模效果一致,并且效率还要高出许多。

由于数组的长度有限,所以难免会出现不同的 Key 通过运算得到的 index 相同,这种情况可以利用链表来解决,HashMap 会在 table[index]处形成链表,采用头插法将数据插入到链表中。

get 方法

get 和 put 类似,也是将传入的 Key 计算出 index ,如果该位置上是一个链表就需要遍历整个链表,通过 key.equals(k) 来找到对应的元素。

遍历方式

1
2
3
4
5
Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Integer> next = entryIterator.next();
System.out.println("key=" + next.getKey() + " value=" + next.getValue());
}
1
2
3
4
5
6
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()){
String key = iterator.next();
System.out.println("key=" + key + " value=" + map.get(key)); }

大专栏  HashMap底层分析>

1
2
3
map.forEach((key,value)->{
System.out.println("key=" + key + " value=" + value);
});

强烈建议使用第一种 EntrySet 进行遍历。

第一种可以把 key value 同时取出,第二种还得需要通过 key 取一次 value,效率较低, 第三种需要 JDK1.8 以上,通过外层遍历 table,内层遍历链表或红黑树。

notice

在并发环境下使用 HashMap 容易出现死循环。

并发场景发生扩容,调用 resize() 方法里的 rehash() 时,容易出现环形链表。这样当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标时就会出现死循环。

所以 HashMap 只能在单线程中使用,并且尽量的预设容量,尽可能的减少扩容。

JDK1.8 中对 HashMap 进行了优化:
hash 碰撞之后写入链表的长度超过了阈值(默认为8),链表将会转换为红黑树

假设 hash 冲突非常严重,一个数组后面接了很长的链表,此时重新的时间复杂度就是 O(n)

如果是红黑树,时间复杂度就是 O(logn)

大大提高了查询效率。

附录:

什么是HashMap?

高并发下的HashMap

什么是ConcurrentHashMap?

什么是红黑树?

HashMap底层分析的更多相关文章

  1. HashMap 底层分析

    以下基于 JDK1.7 分析 如图所示,HashMap底层是基于数组和链表实现的,其中有两个重要的参数: ---容量 ---负载因子 容量的默认大小是16,负载因子是0.75,当HashMap的siz ...

  2. HashMap底层原理分析(put、get方法)

    1.HashMap底层原理分析(put.get方法) HashMap底层是通过数组加链表的结构来实现的.HashMap通过计算key的hashCode来计算hash值,只要hashCode一样,那ha ...

  3. Java——HashMap底层源码分析

    1.简介 HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的. HashMap 最多只允许一条记录的key为 nu ...

  4. ArrayList、LinkedList、HashMap底层实现

    ArrayList 底层的实现就是一个数组(固定大小),当数组长度不够用的时候就会重新开辟一个新的数组,然后将原来的数据拷贝到新的数组内. LinkedList 底层是一个链表,是由java实现的一个 ...

  5. HashMap的分析(转)

    一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap  ...

  6. HashMap底层结构、原理、扩容机制

    https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...

  7. HashMap底层数据结构和算法解析

    1.Hash Map的数据结构? A:哈希表结构(链表散列:数组+链表)实现,结合数组和链表的优点.当链表长度超过8时,链表转换为红黑树. transient Node<K,V>[] ta ...

  8. hashMap 底层原理+LinkedHashMap 底层原理+常见面试题

    1.源码 java1.7    hashMap 底层实现是数组+链表 java1.8 对上面进行优化  数组+链表+红黑树 2.hashmap  是怎么保存数据的. 在hashmap 中有这样一个结构 ...

  9. [转]java 的HashMap底层数据结构

    java 的HashMap底层数据结构   HashMap也是我们使用非常多的Collection,它是基于哈希表的 Map 接口的实现,以key-value的形式存在.在HashMap中,key-v ...

随机推荐

  1. C++读取数量不定的数据

    #include <iostream> using namespace std; int main(){ ,num=; while(cin >> num){//此表达式从标准输 ...

  2. unity学习 5.x依赖打包和解包

    unity5已经封装好了接口,所以依赖打包并没有那么神秘和复杂了. 打包: 1.定义好资源的assetBundleName 2.BuildPipeline.BuildAssetBundles,指定资源 ...

  3. Akka Typed系列:协议&行为

    引言 2019年11月6号LightBend公司发布了AKKA 2.6版本,带来了类型安全的actor,新的Akka Cluster底层通信设施——Artery,带来了更好的稳定性,使用Jackson ...

  4. linux安装nginx步骤

    转载自:https://blog.csdn.net/t8116189520/article/details/81909574,修改部分内容 本文已收录至博客专栏linux安装各种软件及配置环境教程中 ...

  5. 14 微服务电商【黑马乐优商城】:day06-使用nginx反向代理并掌握cors解决跨域

    本项目的笔记和资料的Download,请点击这一句话自行获取. day01-springboot(理论篇) :day01-springboot(实践篇) day02-springcloud(理论篇一) ...

  6. matlab2016b

    http://www.cnblogs.com/CQBZOIer-zyy/p/5933954.html

  7. 关于前端Dom的总结

    简介 DOM (Document Object Model) 文档对象模型 DOM思想使用节点树(node tree)的概念来描述一个HTML页面,页面中的每一个元素.属性.文本都被认为是节点.此外, ...

  8. Linux系统cp:ommiting directory xxx问题解决

    在linux下执行复制命令:  cp nginx-1.16.0 nginx-1.16.0-recovery 报错:  cp: omitting directory ‘./nginx-1.16.0’ 出 ...

  9. python学习笔记(30)——ddt

    1.ddt模块包含类的装饰器ddt和两个方法装饰器data ddt.ddt:装饰类,也就是继承TestCase的类. ddt.data:装饰测试方法,参数是一系列的值,用来传递参数 ddt.file_ ...

  10. Ret2shellcode

    利用原理 ret2shellcode,即控制程序执行 shellcode 代码.一般来说,shellcode 需要我们自己填充.这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码 ...