谈谈 HashMap 的特性

  1. 存储KV键值对, 实现快速存取, key和value都允许为null. key值唯一, 重复则覆盖.
  2. key为null时, 内部使用的是0这个值
  3. 底层数据结构是数组
  4. 非线程安全
  5. 无顺序

谈谈 HashMap 的工作机制

谈谈 HashMap 的底层原理

  1. 从 JDK8 开始采用数组 + 链表 + 红黑树的数据结构, 一直到JDK11基本没变
  2. 存储对象时, 先对键做 hash 计算(基于hashCode), 得到它在bucket数组中的位置, 存储Entry对象
  3. 获取对象时, 同样地先定位到bucket的位置, 再通过键对象的equals()方法找到正确的键值对, 返回值对象

HashMap 中 get 是如何实现的

先对键做 hash 计算(基于hashCode), 得到它在bucket数组中的位置, 然后在这个bucket的树或者链表中遍历查找, 直到找到满足 equals 的节点.

HashMap 中 put 是如何实现的

  1. 计算关于key的hashcode(与Key.hashCode的高16位做异或运算)
  2. 散列表为空时调用resize()初始化散列表
  3. 如果没有发生碰撞,直接添加元素到散列表
  4. 如果发生了碰撞(hashCode值相同), 进行三种判断
    • 若key地址相同或者equals后内容相同,则替换旧值
    • 如果是红黑树结构,就调用树的插入方法
    • 如果是链表结构, 循环遍历: 若遍历到有节点与插入元素的哈希值和内容相同则进行覆盖; 若无相同则尾插法进行插入,插入之后判断链表个数是否到达变成红黑树的阈值8.
  5. 如果桶容量超过阀值,则resize进行扩容

HashMap 的大小超过了负载因子定义的容量会怎样处理?

HashMap 的扩容是如何工作的

扩容的时机

HashMap 在初始化数组Table和当数组Table容量达到阈值时, 在putVal函数中触发扩容

阈值的计算

size > load factor * capacity

扩容的过程

扩容需要重新分配一个新数组, 新数组长度是旧数组的2倍, 遍历旧数组,将元素重新hash分配到新结构中去.

HashMap 中 hash 函数是怎么实现的?

hash的计算方法是: 对key对象计算hashCode, 再将 hashCode 与 hashCode 的高16bit做XOR. 如果key是null, 则直接返回0.

以下详细说明

hash 函数的代码

hash计算是 HashMap 实现机制中很重要的一环, 对任何对象(包括null), 返回一个int类型的hash值. 其实现的代码为

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Object 的 hashCode()

底层的实现, 实际上是 Object 的 hashCode() 函数, 这个函数是native的, 由 C/C++ 提供

public native int hashCode();

hashCode()这个函数有三个特性:

  1. 对同一个对象多次调用, 返回的结果一定相同
  2. 对满足equals(Object)的两个对象分别调用, 返回的结果一定相同
  3. 对于不满足equals(Object)的两个对象分别调用, 返回的结果不一定不同

为什么要对 hashCode() 的结果做高16位的异或运算

  1. h >>> 16是无符号位移运算, Java的int是4个字节16bit, 这个运算将高16bit移动到低16bit
  2. HashMap 默认初始化容量为16, 每次自动扩展或者是手动初始化容量时, 必须是2的N次幂
  3. 将结果与自身的高16bit进行XOR运算, 因为HashMap的增长使用容量是2的N次幂, 将这个作为掩码, 如果hash值只在比这个掩码更高位的bit上有变化, 那么产生的结果总是碰撞的. 这时候需要将高位的变化体现到低位上降低碰撞的概率
  4. 位移和XOR是代价最低的运算. XOR some shifted bits in the cheapest possible way to reduce systematic lossage.

在低位产生大量碰撞的例子

如果不做高位异或会造成碰撞的例子是均匀增长的浮点数, 参考以下代码

public static void main(String[] args) {
float i;
for (i = 0.1f; i < 1000; i++) {
System.out.println(Float.valueOf(i).hashCode());
}
}

i的初始值可以修改为0, 0.1f以及其他值, 观察输出, 可以看到在某些小数部分的情况下(例如0和0.1f), 最低位的结果是固定的0, 2, 4, 6, 8. 如果将上面的循环步长调整为5,

public static void main(String[] args) {
float i;
for (i = 0.1f; i < 1000; i = i + 5) {
System.out.println(Float.valueOf(i).hashCode());
}
}

可以看到在输出的后半部分, 结果的个位全部为8

...
1146963558
1147045478
1147127398
1147209318
1147291238
1147373158
1147455078

如果在实际应用中正好碰到了这种情况, HashMap 的效率将变得非常低. 如果是 JDK7 没有红黑树结构, 时间复杂度就是O(n), JDK8红黑树结构下对应的时间复杂度也是O(logn).

默认容量为什么是2的N次幂?

为了数据的均匀分布,减少哈希碰撞

确定数组位置是用的位运算, 若数据不是2的次幂, 则会增加哈希碰撞的次数和浪费数组空间

HashMap和HashTable的区别?

相同点

都是存储key-value键值对

不同点

  • HashMap 允许 Key-value为null, HashTable不允许
  • HashMap 线程不安全, HashTable 线程安全的
  • HashMap 继承于AbstractMap, HashTable继承于Dictionary
  • HashMap 的迭代器(Iterator)是fail-fast迭代器, 而 Hashtable 的enumerator迭代器不是fail-fast的. 所以当有其它线程改变了 HashMap 的结构(增加或者移除元素), 将会抛出 ConcurrentModificationException.
  • 容量的初始值和增加方式不一样: HashMap 默认的容量是16, 扩容时每次翻倍. Hashtable 默认容量是11, 扩容时每次将容量变为(原容量 x 2 + 1)
  • Hash值算法不同: HashMap使用自定义的hash方法, Hashtable 直接采用的key的hashCode()

谈谈 HashMap 的 loadFactor 参数的作用

loadFactor 是 HashMap 的负载因子, 表示数组的使用率上限. 默认为0.75, 当容纳的元素已经达到数组长度的75%时, 就会进行扩容

HashMap的缺点, JDK8 为什么引入红黑树?

JDK7的 HashMap 实现是数组+链表, 因为哈希计算存在碰撞的可能性, 当大量的元素都存放到同一个桶中时, 就会形成一条很长的链表, 这时 HashMap 读取时就需要遍历这个链表, 时间复杂度就是 O(n). JDK8 引入红黑树是为了解决这个问题, 将查找时间复杂度为优化到 O(logn).

Java语法专题3: HashMap的更多相关文章

  1. Java语法专题1: 类的构造顺序

    合集目录 Java语法专题1: 类的构造顺序 问题 下面的第二个问题来源于Oracle的笔试题, 非常经典的一个问题, 我从07年开始用了十几年. 看似简单, 做对的比例不到2/10. 描述一下多级继 ...

  2. Java语法专题2: 类变量的初始化顺序

    合集目录 Java语法专题2: 类变量的初始化顺序 问题 这也是Java面试中出镜率很高的基础概念问题 描述一下多级继承中字段初始化顺序 描述一下多级继承中类变量初始化顺序 写出运行以下代码时的控制台 ...

  3. Java基础专题

    Java后端知识点汇总——Java基础专题 全套Java知识点汇总目录,见https://www.cnblogs.com/autism-dong/p/11831922.html 1.解释下什么是面向对 ...

  4. Java语法知识总结

    一:java概述: 1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒.PDA等的微处理器: 1994年将Oak语言更名为Java: ...

  5. 深入理解java虚拟机(十二) Java 语法糖背后的真相

    语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言 ...

  6. Java 语法清单

      Java 语法清单 Java 语法清单翻译自 egek92 的 JavaCheatSheet,从属于笔者的 Java 入门与实践系列.时间仓促,笔者只是简单翻译了些标题与内容整理,支持原作者请前往 ...

  7. Java语法

    java语法: 一个java程序可以说是一系列对象的集合,而这些对象都要通过调用彼此的方法来协同工作. 对象: 对象是一个实例,例如:一只猫,它是一个对象,有状态和行为.它的状态状态有:颜色,名字,品 ...

  8. (转)Java集合框架:HashMap

    来源:朱小厮 链接:http://blog.csdn.net/u013256816/article/details/50912762 Java集合框架概述 Java集合框架无论是在工作.学习.面试中都 ...

  9. Java语法糖1:可变长度参数以及foreach循环原理

    语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的 ...

  10. Java集合框架:HashMap

    转载: Java集合框架:HashMap Java集合框架概述   Java集合框架无论是在工作.学习.面试中都会经常涉及到,相信各位也并不陌生,其强大也不用多说,博主最近翻阅java集合框架的源码以 ...

随机推荐

  1. SD协议-时序02

    SD Bus PAD internal card clock - 对于SD card来讲,时钟信号是一个输入 Data0-3 - inout类型,既可能是输入,又可能是输出 对于Data0-3输出的时 ...

  2. [转帖]oracle导出千万级数据为csv格式

    当数据量小时(20万行内),plsqldev.sqlplus的spool都能比较方便进行csv导出,但是当数据量到百万千万级,这两个方法非常慢而且可能中途客户端就崩溃,需要使用其他方法. 一. sql ...

  3. pgcacher 的简单学习

    pgcacher 的简单学习 学习地址 https://github.com/rfyiamcool/pgcacher https://zhuanlan.zhihu.com/p/551833981 ht ...

  4. [转帖]SQL Server超过了每行的最大字节数(8060)的原因和解决办法

    一.现象     出现这种错误都发生在SQL语句建表时,错误提示: "警告: 已创建表 'XXXX,但其最大行大小(10438)超过了每行的最大字节数(8060).如果结果行长度超过 806 ...

  5. 【转帖】Linux开发工具 — readelf、objdump、hexdump

    本博文的主要内容是:1)readelf工具查看ELF文件的信息:2)hexdump工具查看这块内存:3)objdump工具对文件进行反汇编. 前一段时间对Linux不熟,所以很多命令不知道.学习C时候 ...

  6. Nginx的Keepalive的简单学习

    摘要 最近发现某项目的Nginx负载服务器上面有很多Time_wait的TCP连接 可以使用命令 netstat -n |awk '/^tcp/ {++S[$NF]} END{for (a in S) ...

  7. git日志输出相关命令

    git log 默认输出所有的日志 git log 默认输出所有的日志 git 日志输出--只看最近的两条或者三条 有些时候我们可能只需要看最近的2或者3条日志 git log -2 日志输出--只看 ...

  8. flex 布局子元素被挤压的问题

    Flex 意为 "弹性布局",是一种在开发静态页面过程中常用的布局模式. 开发购物车使用flex布局的时候遇到的一种场景:子元素被挤压 具体如图所示, 当商品名称超出两行文字时显示 ...

  9. ABP vNext系列文章10---分布式事务集成netcore.Cap

    最近项目中要用到分布式事务功能,调研了DTM和Cap,最终确定用Cap来实现,Cap支持最终一致性,项目中采用MQ作为消息中间件,数据库用的mysql,集成步骤如下: 1.在需要发布消息的服务中引入如 ...

  10. 系统Hosts文件原理和应用

    Hosts的概念 Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联"数据库",当用户在浏览器中输入一个需要 ...