Java HashMap的工作原理

Java 中 HashMap 的工作原理 HashMap 是 Java 集合框架中一个非常重要的类,它实现了 Map 接口,用于存储键值对(key-value pairs)。其核心特点是基于哈希表实现,提供了平均时间复杂度为 O(1) 的基本操作。以上就是关于 Java 中 HashMap 的基本原理介绍,了解这些有助于更好地利用 HashMap 提升程序性能。值得注意的是,HashMap 不是线程安全的。若要在多线程环境中使用,可以考虑使用。包装 HashMap,或者使用。,后者提供了更好的并发支持。 阅读详情

面试的时候经常会遇见诸如:“java中的HashMap是怎么工作的”,“HashMap的get和put内部的工作原理”这样的问题。本文将用一个简单的例子来解释下HashMap内部的工作原理。首先我们从一个例子开始,而不仅仅是从理论上,这样,有助于更好地理解,然后,我们来看下get和put到底是怎样工作的。

我们来看个非常简单的例子。有一个”国家”(Country)类,我们将要用Country对象作为key,它的首都的名字(String类型)作为value。下面的例子有助于我们理解key-value对在HashMap中是如何存储的。

1. Country.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package org.arpit.javapostsforlearning;
public class Country {
 
  String name;
  long population;
 
  public Country(String name, long population) {
   super ();
   this .name = name;
   this .population = population;
  }
  public String getName() {
   return name;
  }
  public void setName(String name) {
   this .name = name;
  }
  public long getPopulation() {
   return population;
  }
  public void setPopulation( long population) {
   this .population = population;
  }
 
  // If length of name in country object is even then return 31(any random number) and if odd then return 95(any random number).
  // This is not a good practice to generate hashcode as below method but I am doing so to give better and easy understanding of hashmap.
  @Override
  public int hashCode() {
   if ( this .name.length()% 2 == 0 )
    return 31 ;
   else
    return 95 ;
  }
  @Override
  public boolean equals(Object obj) {
 
   Country other = (Country) obj;
    if (name.equalsIgnoreCase((other.name)))
    return true ;
   return false ;
  }
 
}

如果想了解更多关于Object对象的hashcode和equals方法的东西,可以参考:
java中的hashcode()和equals()方法

2. HashMapStructure.java(main class)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.HashMap;
import java.util.Iterator;
   
public class HashMapStructure {
   
     /**
      * @author Arpit Mandliya
      */
     public static void main(String[] args) {
           
         Country india= new Country( "India" , 1000 );
         Country japan= new Country( "Japan" , 10000 );
           
         Country france= new Country( "France" , 2000 );
         Country russia= new Country( "Russia" , 20000 );
           
         HashMap<country,string> countryCapitalMap= new HashMap<country,string>();
         countryCapitalMap.put(india, "Delhi" );
         countryCapitalMap.put(japan, "Tokyo" );
         countryCapitalMap.put(france, "Paris" );
         countryCapitalMap.put(russia, "Moscow" );
           
         Iterator<country> countryCapitalIter=countryCapitalMap.keySet().iterator(); //put debug point at this line
         while (countryCapitalIter.hasNext())
         {
             Country countryObj=countryCapitalIter.next();
             String capital=countryCapitalMap.get(countryObj);
             System.out.println(countryObj.getName()+ "----" +capital);
             }
         }
   
   
}

现在,在第23行设置一个断点,在项目上右击->调试运行(debug as)->java应用(java application)。程序会停在23行,然后在countryCapitalMap上右击,选择“查看”(watch)。将会看到如下的结构:

从上图可以观察到以下几点:

  1. 有一个叫做table大小是16的Entry数组。

  2. 这个table数组存储了Entry类的对象。HashMap类有一个叫做Entry的内部类。这个Entry类包含了key-value作为实例变量。我们来看下Entry类的结构。Entry类的结构:

1
2
3
4
5
6
7
8
static class Entry implements Map.Entry
{
         final K key;
         V value;
         Entry next;
         final int hash;
         ... //More code goes here
}   `
  1. 每当往hashmap里面存放key-value对的时候,都会为它们实例化一个Entry对象,这个Entry对象就会存储在前面提到的Entry数组table中。现在你一定很想知道,上面创建的Entry对象将会存放在具体哪个位置(在table中的精确位置)。答案就是,根据key的hashcode()方法计算出来的hash值(来决定)。hash值用来计算key在Entry数组的索引。

  2. 现在,如果你看下上图中数组的索引10,它有一个叫做HashMap$Entry的Entry对象。

  3. 我们往hashmap放了4个key-value对,但是看上去好像只有2个元素!!!这是因为,如果两个元素有相同的hashcode,它们会被放在同一个索引上。问题出现了,该怎么放呢?原来它是以链表(LinkedList)的形式来存储的(逻辑上)。

上面的country对象的key-value的hash值是如何计算出来的。

`

<code>Japan的Hash值是95,它的长度是奇数。

India的Hash值是95,它的长度是奇数。

Russia的Hash值是31,它的长度是偶数。

France,它的长度是偶数。
</code>

`

下图会清晰的从概念上解释下链表。

所以,现在假如你已经很好地了解了hashmap的结构,让我们看下put和get方法。

Put :

让我们看下put方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
   * Associates the specified value with the specified key in this map. If the
   * map previously contained a mapping for the key, the old value is
   * replaced.
   *
   * @param key
   *            key with which the specified value is to be associated
   * @param value
   *            value to be associated with the specified key
   * @return the previous value associated with <tt>key</tt>, or <tt>null</tt>
   *         if there was no mapping for <tt>key</tt>. (A <tt>null</tt> return
   *         can also indicate that the map previously associated
   *         <tt>null</tt> with <tt>key</tt>.)
   */
  public V put(K key, V value) {
   if (key == null )
    return putForNullKey(value);
   int hash = hash(key.hashCode());
   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 ;
  }

现在我们一步一步来看下上面的代码。

  1. 对key做null检查。如果key是null,会被存储到table[0],因为null的hash值总是0。

  2. key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引。有时候hash函数可能写的很不好,所以JDK的设计者添加了另一个叫做hash()的方法,它接收刚才计算的hash值作为参数。如果你想了解更多关于hash()函数的东西,可以参考:hashmap中的hash和indexFor方法

  3. indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。

  4. 在我们的例子中已经看到,如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表。

  • 如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。
  • 如果索引上有元素,然后会进行迭代,一直到Entry->next是null。当前的Entry对象变成链表的下一个节点。
  • 如果我们再次放入同样的key会怎样呢?逻辑上,它应该替换老的value。事实上,它确实是这么做的。在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。

Get:

现在我们来看下get方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
   * Returns the value to which the specified key is mapped, or {@code null}
   * if this map contains no mapping for the key.
   *
   * <p>
   * More formally, if this map contains a mapping from a key {@code k} to a
   * value {@code v} such that {@code (key==null ? k==null :
   * key.equals(k))}, then this method returns {@code v}; otherwise it returns
   * {@code null}. (There can be at most one such mapping.)
   *
   * </p><p>
   * A return value of {@code null} does not <i>necessarily</i> indicate that
   * the map contains no mapping for the key; it's also possible that the map
   * explicitly maps the key to {@code null}. The {@link #containsKey
   * containsKey} operation may be used to distinguish these two cases.
   *
   * @see #put(Object, Object)
   */
  public V get(Object key) {
   if (key == null )
    return getForNullKey();
   int hash = hash(key.hashCode());
   for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null ; e = e.next) {
    Object k;
    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
     return e.value;
   }
   return null ;
  }

当你理解了hashmap的put的工作原理,理解get的工作原理就非常简单了。当你传递一个key从hashmap总获取value的时候:

  1. 对key进行null检查。如果key是null,table[0]这个位置的元素将被返回。

  2. key的hashcode()方法被调用,然后计算hash值。

  3. indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。

  4. 在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回Entry对象的value,否则,返回null。

要牢记以下关键点:

  • HashMap有一个叫做Entry的内部类,它用来存储key-value对。
  • 上面的Entry对象是存储在一个叫做table的Entry数组中。
  • table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
  • key的hashcode()方法用来找到Entry对象所在的桶。
  • 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
  • key的equals()方法用来确保key的唯一性。
  • value对象的equals()和hashcode()方法根本一点用也没有。

  • 原文链接: javacodegeeks 翻译: ImportNew.com - miracle1919
    译文链接: http://www.importnew.com/10620.html
归因分析计算因子贡献度常见的方法 在归因分析中,我们一般都需要计算出每个因子的贡献度是多少,比如产品DAU上升,对年龄段维度进行拆解,看是不同年龄段的用户对DAU上升的贡献度是多少,一般根据指标的类型,计算贡献度的方法也不一样,下面就列出一些常见的归因分析贡献度的计算方法。 阅读详情

相关推荐

USB-C快充协议分析仪:QC/PD双协议识别与功率监测设计

USB-C快充协议是现代移动设备能量传输的核心技术标准,其本质是通过物理层电压特征(如DP/DM组合、CC线BMC编码)实现动态电压协商。理解协议握手原理对充电器研发、兼容性测试及故障诊断至关重要。本方案聚焦受电端(Sink)视角,基于FUSB302B与高阻抗DP/DM采样电路,实现QC2.0/QC3.0/QC4+与PD2.0/PD3.0的实时识别与状态机解析;结合INA219高精度电流检测与μs级同步采样,达成±0.06%功率测量精度。典型应用于快充芯片验证、产线协议一致性测试及第三方评测,为USB供电系

weixin_42153793的博客 245

Java HashMap工作原理

基本知识 Map   在HashMap中,有一个继承的接口Map<K,V>,Map接口实际就是映射,通过键来获取值。在Java的官方注释中是这么描述的: An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. 这个的大致意思就是:将键映射到值的对象。映射不能包含重复的键;每个键最多可以映射到一个值。 哈希表/散列表   散列

ifwang的博客 313

什么是以太网链路聚合

以太网链路聚合技术通过将多个物理端口捆绑成逻辑端口,可显著提升网络性能。该技术能增加总带宽(各链路带宽之和)、提高可靠性(故障秒级切换)并实现智能负载分担。主要分为手工模式和基于LACP协议的动态模式,后者能检测链路故障更可靠。配置时需确保接口参数一致、模式匹配,适用于核心交换、服务器接入等场景。华为设备通过创建Eth-Trunk接口、指定LACP模式并添加成员接口即可完成部署。这项成熟技术能以低成本解决带宽瓶颈和可靠性问题,是构建高速稳定网络的关键方案。

lai_2020的博客 675

HashMap能够调用的方法和事例

HashMap能够调用的方法和事例

Fredrik的博客 950

SPSS新手教程—两步聚类之结果解读

在《详解SPSS两步聚类之参数设置》一文中,我们已经了解了两步聚类的优点、分析原理,以及参数设置的技巧。 在本节中,会对IBM SPSS Statistics聚类后的结果进行解读,其中会涉及到最终聚类的结果、聚类的质量、变量重要性、聚类特征的解读。 如图1所示,我们先来回顾下本例数据在参数设置面板中的详细设置。 图1:二阶聚类设置 一、数值结果 根据上述参数设置,得到如下的数值分析结果。 首先看到自动聚类结果,如图2所示为聚类的透视表结果,展示了不同聚类数目下的BIC、BIC变化量、BIC变

nekonekoboom的博客 3万+

从半导体物理到实战:手把手教你测量MOS管体二极管的Vf和Trr

本文深入探讨了MOSFET体二极管的关键参数测量方法,重点讲解了正向压降(Vf)和反向恢复时间(Trr)的测量原理与实战步骤。通过搭建测试平台,使用万用表和示波器等工具,手把手指导如何量化这些影响开关损耗和效率的核心参数,并对比分析了硅基与碳化硅(SiC)器件在体二极管性能上的显著差异。

n7o8p的博客 341

Java09 —— HashMap详细介绍(源码解析)和使用示例

概要 这一章,我们对HashMap进行学习。 我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap。内容包括: 第1部分 HashMap介绍第2部分 HashMap数据结构第3部分 HashMap源码解析(基于JDK1.7.0_45) 第3.1部分 HashMap的“拉链法”相关内容 第3.2部分 HashMap的构造函数 第3...

JasonChen3318的博客 386

倍福CX9020(WINCE 7)控制器使用方法和操作

CX9020系列是倍福控制器中较为经济的,操作系统为Windows Embedded Compact 7(简称WINCE7或WINEC7),相对高端控制器(WINES7以上系统),操作系统古老而简洁,上位机软件的编写困难很多,本文是笔者在使用过程中摸索的流程,旨在为CX9020系列的新用户提供参考,也可供其他CE系统的控制器参考。本文中大部分截图采用倍福中国的培训文档,这是由于笔者是在流程走通后再写这篇文章,不想再恢复出厂设置走一遍截图。因笔者在写这篇文章时使用倍福控制器经验并不丰富,难免有纰漏,请读者批评

weixin_41883890的博客 4287

java中hashmap的实现原理是什么?

HashMap 的实现原理 一、HashMap 概述 HashMap 是基于哈希表的 Map 接口的非同步实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得

gulanga5的博客 4975

吊打BEVFormer!GaussianBeV实战:用3D高斯开启BEV分割新范式

GaussianBeV是一种基于3D高斯建模的BEV分割新方法,在自动驾驶感知任务中实现了SOTA性能。其核心创新在于将场景元素表示为3D高斯分布,通过均值、协方差和权重精确描述物体位置、形状和置信度,相比传统BEV方法具有更强的空间建模能力。文章详细介绍了GaussianBeV的原理、环境配置、训练推理流程及其在自动驾驶和城市3D重建中的应用,并讨论了优化方向和未来发展趋势。该方法在nuScenes等数据集上表现出色,为BEV分割任务提供了新的解决方案。

m0_71589036的博客 106

解决宿主机Ping不通虚拟机的问题

在使用虚拟机的过程中,我们常常会遇到宿主机和虚拟机之间无法相互通信的问题。具体来说,我们要查看虚拟机和宿主机的 IP 地址,然后尝试进行 Ping 测试。在某些情况下,我们可能发现 Ping 虚拟机不通,接下来我将分享如何解决这个问题。

howard2005的专栏 7万+

Java HashMap工作原理及实现

1.参考 (1)Java HashMap工作原理及实现 https://yikun.github.io/2015/04/01/Java-HashMap工作原理及实现/

ly969434341的专栏 580

华为od 面试八股文_C++_02_含答案

华为od 面试八股文_C++_02_含答案

MISAYAONE的博客 3122

理解Java中HashMap的工作原理

Java中的HashMap使用散列来高效的查找和存储值。HashMap内部使用Map.Entry的形式来保存key和value, 使用put(key,value)方法存储值,使用get(key)方法查找值。 理解hashCode() Java中的hashCode()方法,是顶层对象Object中的方法,因此Java中所有的对象都会带有hashCode()方法。 在各种最佳实践中,都

Chenny的blog 573

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

JAVA 中的 HashMap 工作原理

Java 中的 ‌HashMap‌ 是基于哈希表实现的键值对存储结构,通过高效的哈希算法和冲突处理机制实现快速的插入、删除和查找操作。

xiaowangbadan0_0的博客 846

JAVA HashMap 的工作原理(译)

原文链接:http://coding-geek.com/how-does-a-hashmap-work-in-java/#!parentId=11378 大多数Java程序员都使用过或者正在使用Map,特别是HashMap。HashMap虽然简单但却能够非常高效地存取数据。可是有多少人知道HashMap的内部原理呢?为了深入理解HashMap,几天前,我读了java.util.HashM

ChowChowT^T的博客 4412

java HashMap工作原理:不仅仅是HashMap

前言: 几乎所有java程序员都用过hashMap,但会用不一定会说。 近年来hashMap是非常常见的面试题,如何为自己的回答加分?需要从理解开始。 “你用过hashMap吗?”,“怎么用的?”,“为什么要用?” 这些问题是面试中经常碰到的,相信大部分的猿哥都能回答,“用过”,然后回答hashMap的一些特性,如HashMap是以键值对存储,可以接受null键值和值,很快但不安全等等。 但是后面的问题来了: “你知道HashMap的工作原理吗?”,“他的get()方法是怎么实现的?” 到这里有些猿哥可能说

qq417085795的博客 191

java之HashMap 的工作原理及代码实现

https://yikun.github.io/2015/04/01/Java-HashMap工作原理及实现/ 概述 从本文你可以学习到: 什么时候会使用HashMap?他有什么特点? 你知道HashMap的工作原理吗? 你知道get和put的原理吗?equals()和hashCode()的都有什么作用? 你知道hash的实现吗?为什么要这样实现? 如果HashMap的大小超过了负载因子(lo...

qq_35967283的博客 1586
上一篇: java中字符串String的深入浅出
下一篇: java中预处理PrepareStatement为什么能起到防止SQL注入的作用??!!
Java我人生
博客等级 码龄15年 2147粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值