hashCode()方法的作用使用分析

一直以来都想写篇文章来说明下hashCode的作用以及相关的知识,许久没动笔,最近找了点资料,自己整理了一下,于是就诞生了下面的东西!

(1)前言,想要明白hashCode的作用,你必须要先知道Java中的集合。

Java中的集合(Collection)有两类,一类是List,再有一类是Set。  前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。  那么我们怎么判断两个元素是否重复呢? 这就是Object.equals方法了。

通常想查找一个集合中是否包含某个对象,就是逐一取出每个元素与要查找的元素进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则返回否定的信息,如果一个集合中有很多元素譬如成千上万的元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出成千上万个元素进行逐一比较才能得到结论,于是,有人就发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域.

hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

(2)首先,equals()和hashcode()这两个方法都是从object类中继承过来的。  equals()方法在object类中定义如下:

1 public boolean equals(Object obj) { 
2 return (this == obj); 
3 } 

很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法。比 如在String类中如下: 

 

 1 public boolean equals(Object anObject) { 
 2 if (this == anObject) { 
 3    return true; 
 4 } 
 5 if (anObject instanceof String) { 
 6    String anotherString = (String)anObject; 
 7    int n = count; 
 8    if (n == anotherString.count) { 
 9     char v1[] = value; 
10     char v2[] = anotherString.value; 
11     int i = offset; 
12     int j = anotherString.offset; 
13     while (n-- != 0) { 
14      if (v1[i++] != v2[j++]) 
15       return false; 
16      } 
17      return true; 
18     } 
19    } 
20 return false; 
21 } 

 

很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。 我们还应该注意,Java语言对equals()的要求如下,这些要求是必须遵循的:  .1) 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。  .2) 反射性:x.equals(x)必须返回是“true”。  .3) 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。  .4) 还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。  .5) 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。  以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守

(3)其次,hashcode() 方法,在object类中定义如下:  public native int hashCode();  说明它是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖hashcode()方法,比如String、Integer、 Double等这些类都是覆盖了hashcode()方法的。例如在String类中定义的hashcode()方法如下: 

 1 public int hashCode() { 
 2 int h = hash; 
 3 if (h == 0) { 
 4    int off = offset; 
 5    char val[] = value; 
 6    int len = count; 
 7   
 8    for (int i = 0; i < len; i++) { 
 9     h = 31*h + val[off++]; 
10    } 
11    hash = h; 
12 } 
13 return h; 
14 } 

解释一下这个程序(String的API中写到):  s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]  使用 int 算法,这里 s[i] 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)

(4)谈到hashcode()和equals()就不能不说到hashset,hashmap,hashtable中的使用,具体是怎样呢,请看如下分析: Hashset是继承Set接口,Set接口又实现Collection接口,这是层次关系。那么hashset是根据什么原理来存取对象的呢?  在hashset中不允许出现重复对象,元素的位置也是不确定的。在hashset中又是怎样判定元素是否重复的呢?判断两个对象是否相等的规则是:  .1),判断两个对象的hashCode是否相等  如果不相等,认为两个对象也不相等,完毕,如果相等,转入2 .2),判断两个对象用equals运算是否相等  如果不相等,认为两个对象也不相等  如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)  为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,所以必须用第2条准则进行限制,才能保证加入的为非重复元素。  比如下面的代码:

 1 public static void main(String[] args) {
 2     String s1 = new String("zhangsan");
 3     String s2 = new String("zhangsan");
 4     System.out.println(s1 == s2);// false
 5     System.out.println(s1.equals(s2));// true
 6     System.out.println(s1.hashCode());// s1.hashcode()等于s2.hashcode()
 7     System.out.println(s2.hashCode());
 8     Set hashset = new HashSet();
 9     hashset.add(s1);
10     hashset.add(s2);
11     System.out.println(hashset.size());//1
12 }

再看如下一些示例:

几个很简单的示例,说明一些很简单的道理 例一:

 1 package com.itsoft;
 2 import java.util.ArrayList;
 3 import java.util.Collection;
 4 public class Point {
 5 private int x;
 6 private int y;
 7 public Point(int x, int y) {
 8     super();
 9     this.x = x;
10     this.y = y;
11 }
12 public static void main(String[] args) {
13     Point p1 = new Point(3, 3);
14     Point p2 = new Point(5, 5);
15     Point p3 = new Point(3, 3);
16     Collection<Point> collection = new ArrayList<Point>();
17     collection.add(p1);
18     collection.add(p2);
19     collection.add(p3);
20     collection.add(p1);
21     System.out.println(collection.size());//4,结果输出4,以为List中可以有重复元素,而且是有序的。
22 }
23 }

例二(在上例的基础上稍作修改把ArrayList改为HashSet):

 

 1 package com.itsoft;
 2 import java.util.ArrayList;
 3 import java.util.Collection;
 4 import java.util.HashSet;
 5 public class Point {
 6 private int x;
 7 private int y;
 8 public Point(int x, int y) {
 9     super();
10     this.x = x;
11     this.y = y;
12 }
13 public static void main(String[] args) {
14     Point p1 = new Point(3, 3);
15     Point p2 = new Point(5, 5);
16     Point p3 = new Point(3, 3);
17     Collection<Point> collection = new HashSet<Point>();
18     collection.add(p1);
19     collection.add(p2);
20     collection.add(p3);
21     collection.add(p1);
22     System.out.println(collection.size());//3,因为HashSet中不会保存重复的对象,每添加一个元素,先判断,再添加,如果已经存在,那么就不在添加,无序的!
23 }
24 }

 

例三(如果我们需要p1和p3相等呢?就必须重新hashcode()和equal()方法):

 

 1 package com.itsoft;
 2 import java.util.Collection;
 3 import java.util.HashSet;
 4 public class Point {
 5 private int x;
 6 private int y;
 7 public Point(int x, int y) {
 8     super();
 9     this.x = x;
10     this.y = y;
11 }
12 @Override
13 public int hashCode() {
14     final int prime = 31;
15     int result = 1;
16     result = prime * result + x;
17     result = prime * result + y;
18     return result;
19 }
20 @Override
21 public boolean equals(Object obj) {
22     if (this == obj)
23       return true;
24     if (obj == null)
25       return false;
26     if (getClass() != obj.getClass())
27       return false;
28     final Point other = (Point) obj;
29     if (x != other.x)
30       return false;
31     if (y != other.y)
32       return false;
33     return true;
34 }
35 public static void main(String[] args) {
36     Point p1 = new Point(3, 3);
37     Point p2 = new Point(5, 5);
38     Point p3 = new Point(3, 3);
39     Collection<Point> collection = new HashSet<Point>();
40     collection.add(p1);
41     collection.add(p2);
42     collection.add(p3);
43     collection.add(p1);
44     System.out.println(collection.size());//输出2,此时p1和p3是相等的
45 }
46 }

 

例四(如果我们把hashcode()方法去掉看下):

 

 1 package com.itsoft;
 2 import java.util.Collection;
 3 import java.util.HashSet;
 4 public class Point {
 5 private int x;
 6 private int y;
 7 public Point(int x, int y) {
 8     super();
 9     this.x = x;
10     this.y = y;
11 }
12 @Override
13 public boolean equals(Object obj) {
14     if (this == obj)
15       return true;
16     if (obj == null)
17       return false;
18     if (getClass() != obj.getClass())
19       return false;
20     final Point other = (Point) obj;
21     if (x != other.x)
22       return false;
23     if (y != other.y)
24       return false;
25     return true;
26 }
27 public static void main(String[] args) {
28     Point p1 = new Point(3, 3);
29     Point p2 = new Point(5, 5);
30     Point p3 = new Point(3, 3);
31     Collection<Point> collection = new HashSet<Point>();
32     collection.add(p1);
33     collection.add(p2);
34     collection.add(p3);
35     collection.add(p1);
36     System.out.println(collection.size());//输出3,此时p1和p3又不相等了
37     //原因:虽然此时p1和p2的equals相等,但是他们的hashcode不相等,所以它们就存储在不同区域,在这两个不同的区域存储着相同的东西,查找的时候只在一个区域查找,就被放进去了。
38 }
39 }

 

注:为了避免第四种情况的发生,通常情况下,一个实例的两个对象equals相同,那么他们的hashcode也必须相等,反之,则不成立,当然,只有对象存储在hash算法系列的集合中,hashcode方法才有价值.这样目的就是确保相同的对象存储在相同的位置。

小结: (1)只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法,即使程序可能暂时不会用到当前类的hashCode方法,但是为它提供一个hashCode方法也不会有什么不好,没准以后什么时候又用到这个方法了,所以,通常要求hashCode方法和equals方法一并被同时覆盖。

(2)equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

提示: (1)通常来说,一个类的两个实例对象用equal方法比较的结果相等时,它们的哈希码也必须相等,但反之则不成立,即equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象的equal方法比较的结果可以不等。

(2)当一个对象被存储进hashset集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进hashset集合时的哈希值就不同了,这种情况下,即使在contains方法使用该对象的当前引用作为的参数去hashset集合中检索对象,也将返回找不到对象的结果,这也会导致无法从hashset集合中单独删除当前对象,从而造成内存泄露,所谓的内存泄露也就说有一个对象不再被使用,但它一直占有内存空间,没有被释放。

 

 

 

 

转载于:https://www.cnblogs.com/androidxiaoyang/archive/2013/04/17/3026420.html

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

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

相关文章

仿个人税务 app html5_【注意】你下载的可能是个假的个税App

新个税法从1月初开始实施。国家税务总局推出“个人所得税”APP&#xff0c;方便纳税人线上填报资料进行专项抵扣。几天来&#xff0c;这款APP的下载量和注册量大幅增长。随之而来的是&#xff0c;很多商业公司制作的各类“个税”APP也成为热门。这其中有不少纯属蹭热点&#xf…

MySQL笔记——多表查询

多表查询不能使用 SELECT * from emp, dept; 会产生笛卡尔积。 笛卡尔积&#xff0c;有A&#xff0c;B两个集合&#xff0c;A中有5条信息&#xff0c;B中有4条信息&#xff0c;那么查询结果就是5*420条一、内连接查询 -- 隐式内连接SELECT 字段列表 FROM 表1,表2,… WHERE 条件…

遇见王沥川的人生感悟_23岁酱油泡饭默默无闻,31岁逆袭人生,王彦霖有何魅力?...

文/小白说娱S姐 原创精品&#xff0c;请勿转载如果兜里只剩下1块钱&#xff0c;生活所迫你会怎样过&#xff1f;王彦霖23岁刚毕业熬过了1元危机&#xff0c;他永远都不会想到当年咬牙坚持熬成就了如今的综艺诸葛。《元气满满的哥哥》连播六期多次排名第一&#xff0c;成为芒果台…

antd vue form 手动校验_参与《开课吧》vue训练营笔记(Day1)

大神说的目标&#xff1a;Vue 挑战20k组件间通信component 官网 详解组件间的传递方式&#xff1a;父传子 直接属性传递子传父 this.$emit 时间传递兄弟组件 利用父组件搭桥组件和子孙 provide / inject子孙 -> 祖先 this.$dispatch 或provide 获取组件元素实例$listeners $…

细学PHP 14 mysql-4

预留转载于:https://www.cnblogs.com/cnmice/archive/2013/04/17/3027102.html

Asterisk针对Mysql的extconfig.conf配置

Asterisk针对Mysql的extconfig.conf配置&#xff0c;这其中涉及到的表的structure [setting] sipusers > mysql,general,sip_buddies sippeers > mysql,general,sip_buddies queues > mysql,general,queue_table queue_members >mysql,general,queue_member_tabl…

ipfs 云服务器_IPFS加速云服务生机和分布式存储

如果能把一个安卓系统或IOS系统安装到分布式存储里&#xff0c;不管人们在做什么&#xff0c;各项数据都可以通过面部识别就可以确认ID&#xff0c;数据的响应性快&#xff0c;安全性和便捷性更高。IPFS的节点上&#xff0c;未来访问网站不再需要HTTP&#xff0c;而是IPFS。分布…

MySQL笔记——打开日志

在my.ini文件中输入如下命令&#xff1a;log_outputFILE general_logon general_log_file"D:\\SoftwareTools\\Mysql\\mysqllog\\mysql.log" slow_query_logon long_query_time 2 slow_query_log_file"D:\\SoftwareTools\\Mysql\\mysqllog\\mysql_slow.log"…

链表怎么输出最后一个元素无空格_剑指offer系列----从尾到头打印链表

从尾到头打印链表信息卡片时间&#xff1a;2020-03-23题目&#xff1a;从尾到头打印链表tag&#xff1a;list题目描述输入一个链表&#xff0c;按链表从尾到头的顺序返回一个 ArrayList。01调用 reverse 函数解题思路这是一种简单粗暴的解法。先遍历一遍链表&#xff0c;在遍历…

a20_v2.0_k70运行在xhda20开发板

修改sys_config.fex文件两个地方 [clock] pll3 297 pll4 300 pll6 600 pll7 297 pll8 336 [dram_para] dram_baseaddr 0x40000000 dram_clk 408 d…

EntiyFramework :Update model from database引起的两个问题

EntiyFramework一大特点就是Code first&#xff0c;但难免有时候因特殊原因需要Update model from database。此次使用该功能时遇到两个问题&#xff0c;且记之。 [问题一] Error 3027: No mapping specified for the following EntitySet/AssociationSet - XXXXXXX. [起因] 在…

MySQL笔记——JDBC入门

&#xff08;一&#xff09;JDBC简介 JDBC概念&#xff1a;&#xff08;1&#xff09;JDBC就是使用Java语言操作关系型数据库的一套API&#xff08;2&#xff09;全称&#xff1a;&#xff08;Java DataBase Connectivity&#xff09;Java数据库连接&#xff08;二&#xff09;…

python统计汉字个数是_Python中文词频统计

今天看到的一个统计&#xff0c;统计的金庸小说里面的高频词语。想着看了一周python&#xff0c;试试看能不能统计。 网上找的代码&#xff0c;调整顺序拼接了一下&#xff0c;分词库是结巴分词。 解决了python2.7中字典显示中文乱码的问题 分词代码&#xff1a;https://github…

cpu个数核数查看 服务器_服务器如何查看cpu核数

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":6,"count":6}]},"card":[{"des":"云服务器 ECS(Elastic Compute Service)是一…

SQL语句大全-珍藏首选

下列语句部分是Mssql语句&#xff0c;不可以在access中使用。 SQL分类&#xff1a; DDL—数据定义语言(CREATE&#xff0c;ALTER&#xff0c;DROP&#xff0c;DECLARE)   DML—数据操纵语言(SELECT&#xff0c;DELETE&#xff0c;UPDATE&#xff0c;INSERT)   DCL—数据控制…

Maven笔记——maven下载与配置

&#xff08;一&#xff09;Maven简介 Apache Maven是一个项目管理和构建的工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff09;的概念。通过一小段描述信息来管理项目的构建&#xff0c;报告和文档。○ 项目对象模型○ 依赖管理模型○ 插件• 仓库分类&#xff1a…

Telerik Reporting之生成报表

本文为原创文&#xff0c;难免会有一些小得瑕疵&#xff0c;敬请谅解。所有示例均是博主测试过的&#xff0c;如有转载请标明出处&#xff0c;谢谢。第一步&#xff1a;建立一个普通类库。 第二步&#xff1a;在此类库中&#xff0c;新建项——Telerik Report报表。 第三步&…

python相对路径下的shell_shell,python获取当前路径(脚本的当前路径) (aso项目记录)...

一、shell获取脚本当前路径 cur_dir$(cd "$(dirname "$0")"; pwd) #获取当前脚本的绝对路径&#xff0c;参数$0是当前脚本对象 等同于cd dirname $0; pwd 但是&#xff0c;cd "dirname $0";pwd是错的&#xff0c;因为dirname不能用双引号 代码实…

spark 用户画像挖掘分析_如何基于Spark进行用户画像?

近期&#xff0c;comSysto公司分享了该公司研发团队利用Spark平台解决Kaggle竞赛问题的经历&#xff0c;为Spark等平台应用于数据科学领域提供了借鉴。主办方提供了一个包含5万个匿名驾驶员线路的数据集&#xff0c;竞赛的目的是根据路线研发出一个驾驶类型的算法类签名&#x…

Maven笔记——依赖管理

使用坐标导入jar包1) 在pom.xml中编写<dependencies>标签2) 在<dependencies>标签中使用<dependency>引入坐标3) 定义坐标的groupId、artifactId、version<dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->…