首先需要明白 hashCode() 和equals是Object类中已经被定义好的,所以在java中定义的任何类都有这两个方法。其中原始的equals()方法是用来比较两个对象的地址值,而原始的hashCode()方法返回其对象所在的物理地址。看下面一个例子:

   public static void main(String[] args) {Person person1 = new Person(10,"zhangsan");Person person2 = new Person(10,"zhangsan");System.out.println("pserson1 hasCode:" + person1.hashCode());System.out.println("pserson2 hasCode:" + person2.hashCode());System.out.println(person1.equals(person2));}
}
class Person{int age;String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(int age, String name) {this.age = age;this.name = name;}

运行结果如下:

需要注意的一点是,equals()相等的两个对象,hashCode()一定相等,hashCode()相等的话,并一定是相等的两个对象,即equals并一定相等(我的理解是equals比较的是两个对象,而hasCode()是对象的属性,对象相等那么其属性一定相等,相反其属性相等但是并不一定是同一个对象)。这就要求我们在重写自定义类的时候如果重写了equals()方法的话,一定也要重写hascode()方法。

同时我们知道Set集合是不允许有重复的内容的。具体判断set集合中是否有已经有该对象的步骤如下:

1)、判断两个对象的hashCode是否相等 。
      如果不相等,认为两个对象也不相等,完毕 
      如果相等,转入2)
(这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。) 
2)、判断两个对象用equals运算是否相等 。
      如果不相等,认为两个对象也不相等 
      如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)

如果想查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?
你通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回否定的信息,如果一个集合中有很多个元素,比如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从集合中取出一万个元素进行逐一比较才能得到结论。

有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组(使用不同的hash函数来计算的),每组分别对应某个存储区域,根据一个对象的哈希吗就可以确定该对象应该存储在哪个区域HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余(这种的hash函数是最简单的)的方式对哈希码进行分组和划分对象的存储区域;Object类中定义了一个hashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获得该对象的哈希码表,然后根据哈希吗找到相应的存储区域,最后取得该存储区域内的每个元素与该对象进行equals方法比较;这样就不用遍历集合中的所有元素就可以得到结论,可见,HashSet集合具有很好的对象检索性能,但是,HashSet集合存储对象的效率相对要低些,因为向HashSet集合中添加一个对象时,要先计算出对象的哈希码和根据这个哈希码确定对象在集合中的存放位置为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,他们的哈希码也必须相等;也就是说,如果obj1.equals(obj2)的结果为true,那么以下表达式的结果也要为true:
obj1.hashCode() == obj2.hashCode()
换句话说:当我们重写一个对象的equals方法,就必须重写他的hashCode方法,不过不重写他的hashCode方法的话,Object对象中的hashCode方法始终返回的是一个对象的hash地址,而这个地址是永远不相等的。所以这时候即使是重写了equals方法,也不会有特定的效果的,因为hashCode方法如果都不想等的话,就不会调用equals方法进行比较了,所以没有意义了。

如果一个类的hashCode()方法没有遵循上述要求,那么,当这个类的两个实例对象用equals()方法比较的结果相等时,他们本来应该无法被同时存储进set集合中,但是,如果将他们存储进HashSet集合中时,由于他们的hashCode()方法的返回值不同(Object中的hashCode方法返回值是永远不同的),就是说可能会被set保存两次的。

看下面这个例子:

public static void main(String[] args) {Person person1 = new Person(10,"zhangsan");Person person2 = new Person(10,"zhangsan");System.out.println("pserson1 hasCode:" + person1.hashCode());System.out.println("pserson2 hasCode:" + person2.hashCode());System.out.println(person1.equals(person2));Set<Person> set = new HashSet<Person>();set.add(person1);set.add(person2);System.out.println(set.size());}
}
class Person{int age;String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(int age, String name) {this.age = age;this.name = name;}@Overridepublic boolean equals(Object o){Person a = (Person) o;return a.name.equals(name) ;}//@Override//public int hashCode(){//    return 1;//}

这里面我们只是重写了equals方法,调用 person1.equals(person2) 也是返回true的,但是最终set.size()是2。这是运行结果

我们再修改一下例子,重写一下 hascode方法

 @Overridepublic int hashCode(){return age + name.hashCode();}

再看一下结果

我们再来看一个有意思的事情,如果我们把equals直接返回false,那么再调用set.add(person1),那么按照我们以上的分析,先检查hashCode() 是否相等,再调用equals()方法,那么这时候应该返回 set.size() 为3,然而事情的真相真如我们分析的那样么,先看运行结果;

 public static void main(String[] args) {Person person1 = new Person(10,"zhangsan");Person person2 = new Person(10,"zhangsan");System.out.println("pserson1 hasCode:" + person1.hashCode());System.out.println("pserson2 hasCode:" + person2.hashCode());System.out.println(person1.equals(person2));Set<Person> set = new HashSet<Person>();set.add(person1);set.add(person2);set.add(person1);// System.out.println(person1 == person1);
        System.out.println(set.size());}
}
class Person{int age;String name;public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Person(int age, String name) {this.age = age;this.name = name;}@Overridepublic boolean equals(Object o){//Person a = (Person) o;//return a.name.equals(name) ;return false;}@Overridepublic int hashCode(){return age + name.hashCode();}

hashCode是基于hashMap实现的,我们查看一下hashMap的源码查找一下原因

/*** 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);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;}

分析一下 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 如果 hash一样的话,会去判断 key == key 和 key.equals(k),只要两个有一个符合要求就插入不成功,而对于同一个对象来说  person1 == person1 永远是true,所以set里面还是2,当然这种情况很少会遇到,一般重写 equals()方法的时候不会直接返回 false~。如果非要让set变成3个话也行,重写一下hashCode就可以了,比如我们可以产生一个随机数

 @Overridepublic int hashCode(){return (int)(Math.random()*100);}

最后在看一个删除时候应该注意的地方:

public static void main(String[] args) {Person person1 = new Person(10, "zhangsan");Person person2 = new Person(10, "zhangsan");//System.out.println("pserson1 hasCode:" + person1.hashCode());//System.out.println("pserson2 hasCode:" + person2.hashCode());//System.out.println(person1.equals(person2));Set<Person> set = new HashSet<Person>();set.add(person1);set.add(person2);set.add(person1);System.out.println("删除前set.size():" + set.size());System.out.println("更改属性前person1.hashCode():" + person1.hashCode());person1.setAge(6);System.out.println("更改属性后person1.hashCode():" + person1.hashCode());set.remove(person1);// System.out.println(person1 == person1);System.out.println("删除后set.size():" + set.size());}

运行结果:

事情又出乎我们的意料之外,我们明明已经删除了person1,但是事实上并没有删除成功,我在程序中已经把原因打印出出来了,更改了属性以后,因为我们的hashCode()是根据属性值来生成的,因此属性更改以后hashCode()也被修改了但是他的存储位置不会有变化,当删除的时候会按照新的hashCode()去找person1,肯定找不到,所以并没有删除成功,会导致失败。

这告诉我们一个事情:如果对象的属性值参与了hashCode的运算中,在进行删除的时候,就不能对其属性值进行修改,否则会出现严重的问题。

转载于:https://www.cnblogs.com/l919310075/p/7376229.html

关于 hashcode 和 equals相关推荐

  1. hashcode的作用_看似简单的hashCode和equals面试题,竟然有这么多坑!

    hashCode()方法和equals()区别与联系这到面试题,看似简单,根据以往面试星友的情况来说,绝大部分人都不能很好的回答出来,要么没有逻辑,想到一句就说一句,要么抓不住重点,答非所问.从这个很 ...

  2. 理解Java中的hashCode 和 equals 方法

    2019独角兽企业重金招聘Python工程师标准>>> 在Java里面所有的类都直接或者间接的继承了java.lang.Object类,Object类里面提供了11个方法,如下: 1 ...

  3. java中Object类的hashCode和equals及toString方法。

    java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...

  4. 为什么使用HashMap需要重写hashcode和equals方法_为什么要重写 hashcode 和 equals 方法?...

    1. 通过Hash算法来了解HashMap对象的高效性 2. 为什么要重写equals和hashCode方法 3. 对面试问题的说明 <Java 2019 超神之路> <Dubbo ...

  5. HashMap存自定义对象为什么要重写 hashcode 和 equals 方法?

    HashMap的k放过自定义对象么? 当我们把自定义对象存入HashMap中时,如果不重写hashcode和equals这两个方法,会得不到预期的结果. class Key{private Integ ...

  6. (转)从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节

    背景:学习java的基础知识,每次回顾,总会有不同的认识.该文系转载 最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的, ...

  7. java中hashcode()和equals()的详解[转]

    今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36).  1. 首先equals()和hash ...

  8. 【面试题】hashCode() 和 equals() 之间的关系

    前言 关于 hashCode 和 equals 的处理,遵循如下规则: 只要重写 equals,就必须重写 hashCode 因为 Set 存储的是不重复的对象,依据 hashCode 和 equal ...

  9. 为什么要重写 hashcode 和 equals 方法?

    我在面试Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分,有 ...

  10. 为什么使用HashMap需要重写hashcode和equals方法_为什么要重写hashcode和equals方法?你能说清楚了吗...

    我在面试Java初级开发的时候,经常会问:你有没有重写过hashcode方法?不少候选人直接说没写过.我就想,或许真的没写过,于是就再通过一个问题确认:你在用HashMap的时候,键(Key)部分,有 ...

最新文章

  1. mysql统计今日首充用户_电商用户行为MySQL分析
  2. 装饰者模式(为对象提供加强的接口)
  3. linux之一些比较新但是常用的命令(expr ag tree cloc stat tmux axel)
  4. 【知识补充】对称加密、非对称加密、数字签名与DDoS攻击
  5. 【开发工具】 JEECG_3.7新版开发工具
  6. 【拼爹坑爹不比爹】深入理解css中position属性及z-index属性
  7. 深度学习自学(三):NMS非极大值抑制总结
  8. npm安装为什么要安装gyp各种报错呢
  9. Atitit bootsAtitit bootstrap布局 栅格.docx目录1. 简述container与container-fluid的区别 11.1.1. 在bootstrap中的布局
  10. oracle客户端登录失败,Win7系统配置Oracle客户端连接失败的解决方法
  11. 微信小程序: 赞赏码的长按识别
  12. EPC 的RFID 标准体系
  13. Django框架_day01
  14. Microsoft SQL Server 数据库使用(二)
  15. 蚂蚁森林中能量自动收取
  16. Pintia(拼题A)刷题插件 on VS Code
  17. 7-3 sdust-Java-字符串集合求并集 (25 分)
  18. 六年安卓开发的技术回顾和展望
  19. 2017阿里研发工程师C/C++实习生招聘笔试题
  20. html有序无序标签,HTML标签有序标签和无序标签

热门文章

  1. 人要懂得放下已经发生,却又无法改变的事情
  2. win10系统的“管理员”
  3. python -m参数
  4. 通俗解释指数加权平均
  5. 查看已安装tensorflow版本
  6. TensorFlow简单线性回归
  7. TensorRT深度学习训练和部署图示
  8. Python分析离散心率信号(中)
  9. 双圆弧插值算法(二)
  10. 增强型固态硬盘支持人工智能工作负载