Java面试题之HashMap 的 hash 方法原理是什么

HashMap 的 hash 方法原理是什么

在了解HashMap的原理之前, 我们先看看hash散列表是怎么工作的, 他的原理是什么。

散列表的原理是将关键字通过散列函数映射到固定的位置上, 并对原始值进行处理, 最终得到的值就是我们所说的哈希值, 即在HashMap中所表现出来的值。在JDK1.7之前,HashMap的内部实现方式是数组 + 链表,数组的下标就是哈希值,而链表则是解决哈希冲突的方案。而在JDK1.8之后,由于链表会引起频繁的GC,会对性能有影响,而添加了红黑树的设计,对冲突的解决更加高效。

比如在HashMap中,我们put一个新的键值对时,便会对新的key生成一个哈希值(hashCode),这个hashCode将会指导他在数组中的大致位置, 但有可能会存在相同的hashCode(即hash冲突),因此会通过拉链法(或者是红黑树)来使链表在当前位置上依次挂起来,这样既能保证查询时的O(1)速度,又能避免产生很多碰撞。

具体来说,当我们使用HashMap的put方法时,HashMap首先会对key进行hash计算,得到一个hash值。然后,HashMap会将这个hash值用来决定key-value存放在数组中的位置。

hash方法的实现原理

接下来是讲解hash方法的实现原理,hash方法是用来计算hashCode的,接下来是hash方法的代码实现:

static final int hash(Object key) {
    int h;
    // key.hashCode()计算出key的哈希值h
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

在上面的实现中, 首先对键值调用key.hashCode()方法,确定键值的哈希值h。hashCode()的具体实现可以看做是从对象的物理地址、时间等因素来生成唯一标识。

在key为null的情况下,哈希值设为0。
为什么要进行以下位运算呢,在进行哈希值计算的时候借助到了以下位运算:

h ^ (h >>> 16)

这里是采用了hash分流算法, 具体步骤如下:

  • 按照hashcode和右移16的结果做异或,生成新的哈希值
  • 将新的哈希值除以容量size的余数,得到下标index
  • 如果当前下标没有hash冲突,则直接将其放入slots[index]中;
  • 如果当前下标有hash冲突,则将当前的键值对添加到当前下标对应的链表的最后;

然而hash散列并不是完美的算法, 在极端情况下可能出现hash冲突,即不同的两个key所对应的hash值是相同的,这就需要采取一些特殊处理的策略,例如拉链法或者是一些替代性的算法,这些算法都有其优劣性。对于比较简单的key值集合来说,HashMap可以将一个键值的HashCode直接映射为一个下标,这种情况下性能最好。

下面再举个栗子: 我们以五个关键字生成的hashCode为例,说一下在HashMap中如何定位分组的过程。

String s1= "name";
String s2 = "gender";
String s3 = "age";
String s4 = "addr";
String s5 = "phone";
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
System.out.println(s4.hashCode());
System.out.println(s5.hashCode());

//分别输出如下, 
//名字(hashCode): 3373707
//性别(hashCode): 1967188755
//年龄(hashCode):95458899
//地址(hashCode): 3009914
//电话(hashCode): 3081039

所以, 当存储出现哈希冲突,我们可以通过维护链表的方式解决。例如, 有两个键值对[key1, value1]和[key2, value2],他们都对应了相同的哈希值h。那么,HashMap就将其存储在[h]位置的链表的尾部。这样,查询哈希表中任一键值对时,HashMap首先对该键值计算哈希值,然后找到[h]位置并遍历链表,在链表中找到一个哈希值和要查询的键值的哈希值相同的键值对。 haMap的hash方法原理和hash算法通过详细的讲解,我们已经能够对HashMap有了较全面的认识。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java面试题之HashMap 的 hash 方法原理是什么 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • java 字符串分割的三种方法(总结)

    Java 字符串分割是一种将字符串拆分为多个子字符串的技术。它是一个常见的字符串操作,用于从文本数据中提取所需的信息。 下面是java字符串分割的三种方法及其详细讲解: 方法一:使用split()方法进行分割 Java中String类有一个split()方法,可以根据指定的分隔符将字符串拆分为多个子字符串,并将结果存储在一个数组中。 String str =…

    Java 2023年5月26日
    00
  • Java实现的文件上传下载工具类完整实例【上传文件自动命名】

    这里是Java实现的文件上传下载工具类完整实例【上传文件自动命名】的完整攻略。 1. 实现思路 文件上传下载是Web开发中非常常见的需求,Java提供了丰富的API和工具来实现文件上传下载的功能。这个工具类的实现思路如下: 文件上传:通过Servlet规范提供的HttpServletRequest对象获取上传文件,将上传文件保存到指定目录中,并返回文件保存路…

    Java 2023年5月20日
    00
  • Java SpringMVC实现国际化整合案例分析(i18n)

    Java SpringMVC实现国际化整合案例分析(i18n) 国际化(Internationalization)是指将应用程序设计成可以适应不同的语言和文化环境。在Java SpringMVC中,我们可以使用国际化(i18n)来实现多语言支持。本文将详细讲解Java SpringMVC实现国际化整合的案例分析,并提供两个示例说明。 国际化的实现原理 在Ja…

    Java 2023年5月17日
    00
  • Java实现单人信息管理程序

    下面我将为你详细讲解“Java实现单人信息管理程序”的完整攻略。 1. 需求分析 在开始编写程序之前,我们需要确定具体的需求。本文中,我们需要实现单人信息管理程序,需要实现以下功能:1. 添加一个新的信息2. 查看所有信息3. 修改已有的信息4. 删除已有的信息 2. 数据结构设计 在确定需求之后,我们需要确定数据结构。这里我们使用Java中的ArrayLi…

    Java 2023年5月18日
    00
  • SpringMVC mybatis整合实例代码详解

    SpringMVC MyBatis整合实例代码详解 SpringMVC和MyBatis是两个非常流行的Java Web框架,它们都有自己的优点和特点。在本文中,我们将详细讲解如何将SpringMVC和MyBatis整合起来,以便更好地开发Web应用程序。 整合步骤 整合SpringMVC和MyBatis需要以下步骤: 添加依赖 配置数据源 配置MyBatis…

    Java 2023年5月18日
    00
  • Springboot中如何使用Jackson

    下面就是Spring Boot中如何使用Jackson的完整攻略。 什么是Jackson Jackson是一款用于Java平台的高效、功能强大的JSON库。它可以将Java对象转换为JSON格式的字符串,也可以将JSON格式的字符串转换为Java对象。Jackson是目前Java开发中最受欢迎的JSON库之一。 在Spring Boot中使用Jackson …

    Java 2023年5月19日
    00
  • 浅谈java中六大时间类的使用和区别

    浅谈Java中六大时间类的使用和区别 Java中提供了六种对时间进行处理的类:Date、Calendar、SimpleDateFormat、DateFormat、Duration和Instant。这些类都各自有着不同的用法和适用场景。在本文中,我们将详细讨论这些类的区别和用法。 Date类 Date类是Java中处理日期和时间的最基本的类,它提供了一系列方法…

    Java 2023年6月1日
    00
  • java OOM内存泄漏原因及解决方法

    Java OOM内存泄漏原因及解决方法 前言 Java内存泄漏(Memory Leak)是指程序中已经不再用到的内存,因为某些原因没有被释放,导致这部分内存永远无法被使用,从而引起内存的浪费。内存泄漏会导致系统的性能降低,甚至会导致系统奔溃。下面将详细介绍Java OOM内存泄漏的原因及解决方法。 OOM内存泄漏原因 长生命周期对象持有短生命周期对象的引用 …

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部