HashCode和hashMap、hashTable

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

什么是哈希码(HashCode)

在Java中,哈希码代表对象的特征。

例如对象 String str1 = “aa”, str1.hashCode= 3104

String str2 = “bb”, str2.hashCode= 3106

String str3 = “aa”, str3.hashCode= 3104

根据HashCode由此可得出str1!=str2,str1==str3

下面给出几个常用的哈希码的算法。

1:Object类的hashCode.返回对象的内存地址经过处理后的结构,由于每个对象的内存地址都不一样,所以哈希码也不一样。

2:String类的hashCode.根据String类包含的字符串的内容,根据一种特殊算法返回哈希码,只要字符串所在的堆空间相同,返回的哈希码也相同。

3:Integer类,返回的哈希码就是Integer对象里所包含的那个整数的数值,例如Integer i1=new Integer(100),i1.hashCode的值就是100 。由此可见,2个一样大小的Integer对象,返回的哈希码也一样。


HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键。  

那么Java运行时环境是如何判断HashSet中相同对象、HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢?   

在研究这个问题之前,首先说明一下JDK对equals(Object obj)和hashcode()这两个方法的定义和规范:  

在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。  

equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。  

hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。  

接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个): 

 规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。 

 规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。  

根据这两个规范,可以得到如下推论:  

1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 

 2、如果两个对象不equals,他们的hashcode有可能相等。  

3、如果两个对象hashcode相等,他们不一定equals。  

4、如果两个对象hashcode不相等,他们一定不equals。   

这样我们就可以推断Java运行时环境是怎样判断HashSet和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。

测试程序如下:首先我们定义一个类,重写hashCode()和equals(Object obj)方法 

 class A {           @Override     public boolean equals(Object obj) {System.out.println("判断equals"); return false;         }           @Override     public int hashCode() {     System.out.println("判断hashcode");     return 1;          }     }

然后写一个测试类,代码如下:

public class Test {           public static void main(String[] args) {     Map<A,Object> map = new HashMap<A, Object>();map.put(new A(), new Object());map.put(new A(), new Object());              System.out.println(map.size());}
}

运行之后打印结果是:   

判断hashcode 

判断hashcode  

判断equals 


HashCode的作用

首先,想要明白hashCode的作用,你必须要先知道Java中的集合。
  总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。你知道它们的区别吗?前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。那么这里就有一个比较严重的问题了:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。
    于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。如果详细讲解哈希算法,那需要更多的文章篇幅,我在这里就不介绍了。初学者可以这样理解,hashCode方法实际上返回的就是对象存储的物理地址(PS:这是一种算法,数据结构里面有提到。在某一个地址上(对应一个哈希值,该值并不特指内存地址),存储的是一个链表。在put一个新值时,根据该新值计算出哈希值,找到相应的位置,发现该位置已经蹲了一个,则新值就链接到旧值的下面,由旧值指向(next)它(也可能是倒过来指。。。)。可以参考HashMap)。
    这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。
    所以,Java对于eqauls方法和hashCode方法是这样规定的:
1、如果两个对象相同,那么它们的hashCode值一定要相同;
2、如果两个对象的hashCode相同,它们并不一定相同
    上面说的对象相同指的是用eqauls方法比较。
    你当然可以不按要求去做了,但你会发现,相同的对象可以出现在Set集合中。同时,增加新元素的效率会大大下降。

怎么重写HashCode?

下面介绍如何来重写hashCode()方法。通常重写hashCode()方法按以下设计原则实现。

(1)把某个非零素数,例如17,保存在int型变量result中。

(2)对于对象中每一个关键域f(指equals方法中考虑的每一个域)参照以下原则处理。

boolean型,计算(f?0:1)。

byte、char和short型,计算(int)f。

long型,计算(int)(f^(f>>32))。

float型,计算Float.floatToIntBits(f)。

double型,计算Double.doubleToLongBits(f)得到一个long,再执行long型的处理。

对象引用,递归调用它的hashCode()方法。

数组域,对其中的每个元素调用它的hashCode()方法。

(3)将上面计算得到的散列码保存到int型变量c,然后执行result = 37 * result + c。

(4)返回result。



类 HashMap<K,V>

java.lang.Objectjava.util.AbstractMap<K,V>     java.util.HashMap<K,V>

  • 类型参数:

  • K - 此映射所维护的键的类型

  • V - 所映射值的类型

  • 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

HashCode和HashMap之间的关系

先如下代码:

import java.util.HashMap;  
public class Test {  //重写Equals不重写HashCode  static class Key {  private Integer id;  private String value;  public Key(Integer id, String value) {  super();  this.id = id;  this.value = value;  }  @Override  public boolean equals(Object o) {  if(o == null || !(o instanceof Key)) {  return false;  }else {  return this.id.equals(((Key)o).id);  }  }  }  //重写Equals也重写HashCode  static class Key_ {  private Integer id;  private String value;  public Key_(Integer id, String value) {  super();  this.id = id;  this.value = value;  }  @Override  public boolean equals(Object o) {  if(o == null || !(o instanceof Key_)) {  return false;  }else {  return this.id.equals(((Key_)o).id);  }  }  @Override  public int hashCode() {  return id.hashCode();  }  }  public static void main(String[] args) {  //test hashcode  HashMap<Object, String> values = new HashMap<Object, String>(5);  Test.Key key1 =   new Test.Key(1, "one");  Test.Key key2 =   new Test.Key(1, "one");  System.out.println(key1.equals(key2));  values.put(key1, "value 1");  System.out.println(values.get(key2));  Test.Key_ key_1 =   new Test.Key_(1, "one");  Test.Key_ key_2 =   new Test.Key_(1, "one");  System.out.println(key_1.equals(key_2));  System.out.println(key_1 == key_2);  values.put(key_1, "value 1");  System.out.println(values.get(key_2));  }  
}

输出如下:由上述例子可见:只重写了equasl方法的Key类 在用做Hash中的键值的时候 两个equasl为true的对象不能获取相应 的Value的而重写了hashCode方法和equals方法的key_类 两个相等的对象 可以获取同一个Value的,这样更符合生活中 的逻辑HashMap对象是根据Key的hashCode来获取对应的Vlaue 因而两个HashCode相同的对象可以获取同一个Value

<span style="color:#cc66cc;">  
</span>


转载于:https://my.oschina.net/yongqingfan/blog/628174

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285551.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言试题108之打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数 本身。例如:153 是一个“水仙花数”,因为 153=1 的三次方+5 的三次方+3 的三次方。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该…

【ArcGIS风暴】ArcGIS Editor for OSM中文教程(1):软件下载及安装

OpenStreetMap ArcGIS编辑器允许你使用ArcGIS工具为OpenStreetMap数据工作。这个桌面工具允许你加载OpenStreetMap数据并将其存储在地理数据库中 。你可以使用ArcMap熟悉的编辑环境的创建,修改,做网络分析,或者更新数据。一旦您完成编辑后,你可以回到编辑修改OSM使它们提供…

使用Scrapy时出现虽然队列里有很多Request但是却不下载,造成假死状态

2019独角兽企业重金招聘Python工程师标准>>> DOWNLOAD_TIMEOUT Default: 180 The amount of time (in secs) that the downloader will wait before timing out. Note This timeout can be set per spider using download_timeoutspider attribute and per-request …

为什么 Istio 要使用 SPIRE 做身份认证?

今年 6 月初&#xff0c;Istio 1.14 发布 [1] &#xff0c;该版本中最值得关注的特性是新增对 SPIRE 的支持。SPIFFE[2] 和 SPIRE 都是 CNCF 孵化项目&#xff0c;其中 SPIRE 是 SPIFFE 的实现之一。本文将带你了解 SPIRE 对于零信任架构的意义&#xff0c;以及 Istio 是为何使…

C语言试题106之有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月 后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

1、题目 题目:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月 后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 分析:兔子的规律为数列 1,1,2,3,5,8,13,21… 2 、温馨提示 想获取更多C语言题目请猛搓这里==========》200个C语言…

【ArcGIS风暴】ArcGIS Editor for OSM中文教程(2):下载及加载OSM数据

本文讲解在ArcGIS中借助OpenStreetMap工具下载并加载OSM数据。 文章目录 1. 下载OSM数据2. 加载OSM数据1. 下载OSM数据 在工具箱中双击Download OSM Data(XAPI)工具。 Download URL:http://www.overpass-api.de/api/xapi_meta? Downlaod Extent:与图层lanzhou相同

Object.observe将不加入到ES7

先请看 Object.observe 的 API Object.observe(obj, callback[, acceptList])它用来监听对象的变化&#xff0c;当给该对象添加属性&#xff0c;修改属性时都会被依次记录下来 看一个示例 var person {} Object.observe(person, function(arr) {var change arr[0]console.log…

Kafka学习征途:.NET Core操作Kafka

【Kafka】| 总结/Edison Zhou1可用的Kafka .NET客户端作为一个.NET Developer&#xff0c;自然想要在.NET项目中集成Kafka实现发布订阅功能。那么&#xff0c;目前可用的Kafka客户端有哪些呢&#xff1f;目前.NET圈子主流使用的是 Confluent.Kafkaconfluent-kafka-dotnet : htt…

C语言试题107之判断 101至200 之间有多少个素数,并输出所有素数。

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:判断 101至200 之间有多少个素数,并输出所有素数 分析:判断素数的方法:用一个数分…

C语言将十进制输出二进制、八进制、十六进制的方法总结

文章目录 方法一:直接输出方法二:itoa函数方法一:直接输出 C语言中,控制printf函数输出格式的是格式字符,printf没有直接打出2进制数的格式符,直接打出16进制的格式符是x格式符,即%x。 printf函数中输出的格式为printf("<格式化字符串>", <参量表&…

四叉树算法

2019独角兽企业重金招聘Python工程师标准>>> title: 四叉树算法 date: 2016-1-11 15:10 categories: IOS tags: 算法 小小程序猿我的博客&#xff1a;http://daycoding.com 转载&#xff1a;http://blog.csdn.net/zhanxinhang/article/details/6706217 高德iOS聚合…

2019年中国教育信息化行业研究报告

2019年中国教育信息化行业研究报告 教育行业丨研究报告 本文转自&#xff1a;艾瑞咨询 核心摘要&#xff1a; 教育信息化2.0时代&#xff0c;教育相关政府/学校以更开放的姿态对待社会各类业态的进入&#xff0c;共建共享优质教育资源&#xff0c;提升教育公平与教育质量。同…

C语言试题109之将一个正整数分解质因数。例如:输入 90,打印出 90=2乘3乘3乘5

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:将一个正整数分解质因数。例如:输入 90,打印出 90=233*5。 分析:对 n 进行分解质因…

dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能

我对几个应用进行严格的启动性能评估&#xff0c;对比了在 .NET Framework 和 dotnet 6 下的应用启动性能&#xff0c;非常符合预期的可以看到&#xff0c;在用户的设备上&#xff0c;经过了 NGen 之后的 .NET Framework 可以提供非常优越的启动性能&#xff0c;再加上 .NET Fr…

使用myeclipse建立maven项目(重要)

maven是管理项目的&#xff0c;myeclipse是编写代码的。第一次写项目都要配置好多东西&#xff0c;很麻烦&#xff0c;now 来看看怎样新建一个maven项目。 工具/原料 myeclipsemaven方法/步骤 因为教程使用的maven是自己下载配置的&#xff0c;并没有使用myeclipse自带的&#…

LeetCode 每日一题 Day 22 || 枚举(数学方法)/二分

1954. 收集足够苹果的最小花园周长 给你一个用无限二维网格表示的花园&#xff0c;每一个 整数坐标处都有一棵苹果树。整数坐标 (i, j) 处的苹果树有 |i| |j| 个苹果。 你将会买下正中心坐标是 (0, 0) 的一块 正方形土地 &#xff0c;且每条边都与两条坐标轴之一平行。 给你…

不用@微信官网了,用python给自己的微信头像加个小国旗

国旗LOGO&#xff08;png透明格式&#xff09;&#xff1a; 微信头像 合成结果&#xff1a; import base64 import os import re from io import BytesIO from PIL import Image import tkinter as tk from tkinter import filedialog# 水印图片 可以自己指定 #markImageImage…

C语言试题106之有一对兔子问题

✅作者简介:大家好我是码莎拉蒂,CSDN博客专家🥇🥇🥇 📃个人主页:个人主页 🔥系列专栏:C语言试题200例 💬推荐一款模拟面试、刷题神器👉 点击跳转进入网站 1、题目 题目:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月 后每个月又…

Blazor University (34)表单 —— 获得表单状态

原文链接&#xff1a;https://blazor-university.com/forms/accessing-form-state/获得表单状态源代码[1]有时&#xff0c;我们需要获得 <EditForm> 子内容中的表单状态。最常见的用途是当我们需要访问输入的 CSS 类时&#xff0c;指示输入是否被修改或有效/无效。例如&a…

[转]c# 中间件 的扩展模型(.net webapi/.net Core 的 MiddleWare 处理模型)

在学习 asp.net WebApi 或者asp.net Core 的时候&#xff0c;它们管道的处理模型跟 asp.net MVC/WebForm 的管道模型是不一样的。 asp.net WebApi 或者asp.net Core 他们使用了一种叫做“中间件”的处理模型&#xff0c;相对于传统管道模型&#xff0c;剔除了很多非必要的处理…