目录

  • 数组+链表:存在性能最坏情况O(n)
    • Java7的HashMap的put方法思路
  • 数组+链表+红黑树:性能提高到O(logn)
    • Java8的HashMap的putVal方法思路

数组+链表:存在性能最坏情况O(n)

Java8以前,HashMap底层数据结构采用数组+链表的结构。
数组特点:查询快,增删慢。
链表特点:查询慢,增删较快。
HashMap:结合了数组和链表的优势。同时HashMap的操作是非Synchronized,因此效率比较高。

Java7的HashMap的put方法思路

put源码:

public V put(K key, V value) {if (table == EMPTY_TABLE) {inflateTable(threshold);}if (key == null)return putForNullKey(value);int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}

addEntry源码:

/*** Adds a new entry with the specified key, value and hash code to* the specified bucket.  It is the responsibility of this* method to resize the table if appropriate.** Subclass overrides this to alter the behavior of put method.*/void addEntry(int hash, K key, V value, int bucketIndex) {if ((size >= threshold) && (null != table[bucketIndex])) {resize(2 * table.length);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}createEntry(hash, key, value, bucketIndex);}

方法简述:
1、初始化HashMap,若Entry数组类型的table为空,inflated(膨胀,扩容意思)一个table,threshold默认=初始容量=16;
2、对key求Hash值,然后再计算table下标;
3、如果没有碰撞,即数组中没有相应的键值对,直接放入桶(bucket)中,如果碰撞了,以链表的方式链接到后面;
4、如果键值对已经存在就替换旧值;
5、如果桶满了(容量16*加载因子0.75),就需要扩容resize();

但是,存在坏情况:如果通过哈希散列运算得到的是同一个值,即总是分配到同一个桶中,使某个桶的链表长度很长。
由于链表查询需要从头开始遍历,最坏情况下,HashMap性能变为O(n)。

数组+链表+红黑树:性能提高到O(logn)

Java8以后,HashMap底层数据结构采用数组+链表+红黑树的结构。
通过常量TREEIFY_THRESHOLD=8和UNTREEIFY_THRESHOLD=6来控制链表与红黑树的转化。

Java8的HashMap的putVal方法思路

putVal源码:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

在Java7源码基础上增加了链表和红黑树的转化。
如果链表长度超过阈值8,就把链表转换成红黑树,如果链表长度低于6,就把红黑树转回链表。改变了最坏情况下O(n),性能提高到O(logn)。
注意:Java7中数组里的元素叫Entry,Java8及以后改名为Node(节点)。

HashMap的put方法(Java7)和putVal方法(Java8)相关推荐

  1. 【EventBus】事件通信框架 ( 订阅方法注册 | 检查订阅方法缓存 | 反射获取订阅类中的订阅方法 )

    文章目录 一.检查订阅方法缓存 二.反射获取订阅类中的订阅方法 三.完整代码示例 一.检查订阅方法缓存 注册订阅者时 , 只传入一个订阅者类对象 , 其它信息都需要通过反射获取 ; 1. 获取订阅者类 ...

  2. JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈

    运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...

  3. 为什么重写Equals方法要重写HashCode方法

    目录 1.equals方法 2.hashcode方法 3.hash算法 4.重写equals方法 5.重写HashCode方法 在每个类中,重写equals方法的时侯,一定要重写hashcode方法. ...

  4. Go 学习笔记(34)— Go 方法声明、方法调用、方法值、方法表达式、切片对象方法、指针对象方法

    1. 方法声明 Go 语言的方法非常纯粹, 可以看作特殊类型的函数, 其显式地将对象实例或指针作为函数的第一个参数, 并且参数名可以自己指定, 而不强制要求一定是 this 或 self .这个对象实 ...

  5. main方法_错误: 在类 ZiFUChuan.Pyramid 中找不到 main 方法, 请将 main 方法定义为:

    错误: 在类 ZiFUChuan.Pyramid 中找不到 main 方法, 请将 main 方法定义为: public static void main(String[] args) 否则 Java ...

  6. html中有csstext方法吗,style对象的cssText方法有哪些使用方法

    这次给大家带来style对象的cssText方法有哪些使用方法,style对象的cssText方法使用的注意事项有哪些,下面就是实战案例,一起来看一下. cssText 本质是什么? cssText ...

  7. 15 三明治集成方法和混合策略集成方法

    三明治集成方法和混合策略集成方法 前言 三明治集成方法 混合策略集成方法 总结 前言 关于集成测试方法今天我们再学习两个方法,三明治集成方法和混合策略集成方法. 三明治集成方法 采用三明治方法的优点是 ...

  8. 本地方法接口和本地方法栈

    本地方法接口 什么是本地方法? 一个Native Method就是一个Java调用非Java代码的接口.一个Native Method是这样一个Java方法:该方法的实现由非Java语言实现,比如C. ...

  9. JVM---本地方法接口和本地方法栈

    本地方法接口 什么是本地方法? 简单地讲,一个 Native Method 是一个 Java 调用非 Java 代码的接囗.一个 Native Method 是这样一个 Java 方法:该方法的实现由 ...

最新文章

  1. CentOS 6安装DHCP
  2. 一个基于 Spring Boot 的项目骨架
  3. Error in **: incorrect number of subscripts on matrix
  4. firefox浏览器不能使用window.close的解决方案
  5. php html实例代码,PHP生成HTML静态页面实例代码
  6. 三层架构项目如何发布_以k8s集群管理为例,大牛教你如何设计优秀项目架构
  7. [你必须知道的.NET]第二十回:学习方法论
  8. 这些基因的名字太有才了,研究一下都可以发10分文章
  9. 【转】Android 如何获取SDCard 内存(二)
  10. android listview 不显示_Android使用ExpandableListview实现时间轴
  11. Expected one result (or null) to be returned by selectOne(), but found: 7
  12. ARM指令学习,王明学learn
  13. mysql模式匹配详解_MySQL SQL模式匹配
  14. 大整数乘法——分治算法的时间复杂度
  15. 记一次网络访问故障排查
  16. 开源 == 文化:红帽社区开放日图文回顾
  17. firefox不能正常下载文件
  18. 三种网线的RJ-45接头制作法图解(转)
  19. vue前端自动生成编号或者订单单号(日期+随机数)
  20. K近邻分类器(李飞飞CS231n学习笔记---lecture2:K最近邻算法)

热门文章

  1. tmpfiles.d导致的unix:///tmp/supervisor.sock no such file坑(待续)
  2. Redis配置和常用命令
  3. 控制JSP头部引入外部文件编译后在第一行
  4. PingingLab传世经典系列《CCNA完全配置宝典》-2.7 EIGRP基本配置
  5. [转载]ns2在linux安装过程
  6. ASP“.NET研究”.NET中的认证与授权
  7. 艾伟:WCF从理论到实践(2):决战紫禁之巅
  8. Lotus Notes Domino 监控
  9. android组建之间通信_Android组件化(三)组件之间的通信
  10. 携程Docker实践