下面给出一个实例,重新编写equals()方法,提供自定义的相等标准

  1. public class PersonTest {
  2. public static void main(String[] args) {
  3. Person p1 = new Person("孙悟空", "1234");
  4. Person p2 = new Person("孙行者", "1234");
  5. Person p3 = new Person("孙大圣", "12345");
  6. System.out.println("p1和p2是否相等?" + p1.equals(p2));
  7. System.out.println("p1和p3是否相等?" + p1.equals(p3));
  8. System.out.println("p2和p3是否相等?" + p2.equals(p3));
  9. }
  10. }
  11.  
  12. class Person {
  13. private String name;
  14. private String id;
  15.  
  16. public Person() {
  17. }
  18. public Person(String name, String id) {
  19. this.name = name;
  20. this.id = id;
  21. }
  22.  
  23. public String getId() {
  24. return this.id;
  25. }
  26. public boolean equals(Object obj) {
  27. //如果两个对象为同一个对象
  28. if( this == obj) {
  29. return true;
  30. }
  31. //当obj不为null,其它是Person类的实例时
  32. if( obj != null && obj.getClass() == Person.class) {
  33. Person obj2 = (Person)obj;
  34. //并且当前对象的id与obj对象的id相等才可判断两个对象相等
  35. if (this.getId().equals(obj2.getId())) {
  36. return true;
  37. }
  38. }
  39. return false;
  40. }
  41. }

上述实例运行结果显示:
p1和p2是否相等?true

p1和p3是否相等?false

p2和p3是否相等?false

通常而言,正确地重写equals方法应该满足下列条件:

  • 自反性:对任意x,x.equals(x)一定返回true。
  • 对称性:对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true。
  • 传递性:对任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true。
  • 一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应保持一致,要么一直是true,要么一直是false。
  • 对任何不是null的x,x.equals(null)一定返回false。

特别注意:

重新equals()方法后,一定要重写hashCode()方法,否则会引起一些意想不到的错误。

所以,可以为上面的代码添加如下的hashCode()方法。

  1. public int hashCode() {
  2. final int PRIME = 31;
  3. int result = 1;
  4. result = PRIME * result + getId().hashCode();
  5. return result;
  6. }

注意:不同类型hashCode值的计算可以采用如下公式。

Field类型 计算公式
boolean hashCode=(f?0:1);

整数类型(byte,short,char,int)

hashCode=(int)f;
long   hashCode=(int)(f^(f>>>32));
float hashCode=Float.floatToIntBits(f);
double

long l=Double.doubleToLongBits(f);

hashCode=(int)(l^(l>>>32);

普通引用类型 hashCode=f.hashCode();

使用系数为31的原因如下:

  • 31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终的出来的结果只能被素数本身和被乘数还有1来整除!。(减少冲突)
  • 31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化.(提高算法效率)
  • 选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
  • 并且31只占用5bits,相乘造成数据溢出的概率较小。

实例:

Java中的集合有两类,一类是List,一类是Set。List内的元素是有序的,元素可以重复。Set元素无序,但元素不可重复。

下面,通过一个实例来加深对equals和hashCode方法的理解。

  1. import java.util.HashSet;
  2.  
  3. public class HashSetAndHashCodeTest {
  4. public static void main(String[] args) {
  5. HashSet<Point1> hs1 = new HashSet<Point1>();
  6. Point1 p11 = new Point1(3, 3);
  7. Point1 p12 = new Point1(3, 3);
  8. Point1 p13 = new Point1(3, 5);
  9. hs1.add(p11);
  10. hs1.add(p11);
  11. hs1.add(p12);
  12. hs1.add(p13);
  13. System.out.println(hs1.size()); //答案是3
  14.  
  15. HashSet<Point2> hs2 = new HashSet<Point2>();
  16. Point2 p21 = new Point2(3, 3);
  17. Point2 p22 = new Point2(3, 3);
  18. Point2 p23 = new Point2(3, 5);
  19. hs2.add(p21);
  20. hs2.add(p22);
  21. hs2.add(p23);
  22. System.out.println(hs2.size()); // 答案是2。p21和p22被认为是同一个对象。
  23.  
  24. HashSet<Point3> hs3 = new HashSet<Point3>();
  25. Point3 p31 = new Point3(3, 3);
  26. Point3 p32 = new Point3(3, 3);
  27. Point3 p33 = new Point3(3, 5);
  28. hs3.add(p31);
  29. hs3.add(p32);
  30. hs3.add(p33);
  31. System.out.println(hs3.size()); // 可能是2,可能是3。因为根据内存地址算出的hashcode不知道是否在一个区域。
  32. }
  33. }
  34.  
  35. /**
  36. * 1 没有重写hashCode和equals的方法
  37. */
  38. class Point1 {
  39. private int x;
  40. private int y;
  41.  
  42. public Point1(int x, int y) {
  43. super();
  44. this.x = x;
  45. this.y = y;
  46. }
  47.  
  48. public int getX() {
  49. return x;
  50. }
  51.  
  52. public void setX(int x) {
  53. this.x = x;
  54. }
  55.  
  56. public int getY() {
  57. return y;
  58. }
  59.  
  60. public void setY(int y) {
  61. this.y = y;
  62. }
  63. }
  64.  
  65. /**
  66. * 2 重写hashCode和equals的方法 *
  67. */
  68. class Point2 {
  69. private int x;
  70. private int y;
  71.  
  72. Point2(int x, int y) {
  73. super();
  74. this.x = x;
  75. this.y = y;
  76. }
  77.  
  78. @Override
  79. public int hashCode() {
  80. final int prime = 31;
  81. int result = 1;
  82. result = prime * result + x;
  83. result = prime * result + y;
  84. return result;
  85. }
  86.  
  87. @Override
  88. public boolean equals(Object obj) {
  89. if (this == obj) return true;
  90. if (obj == null) return false;
  91. if (getClass() != obj.getClass()) return false;
  92. Point2 other = (Point2) obj;
  93. if (x != other.x) return false;
  94. if (y != other.y) return false;
  95. return true;
  96. }
  97.  
  98. public int getX() {
  99. return x;
  100. }
  101.  
  102. public void setX(int x) {
  103. this.x = x;
  104. }
  105.  
  106. public int getY() {
  107. return y;
  108. }
  109.  
  110. public void setY(int y) {
  111. this.y = y;
  112. }
  113. }
  114.  
  115. /**
  116. * 3 没有重写hashCode的方法,但重写equals的方法
  117. */
  118. class Point3 {
  119. private int x;
  120. private int y;
  121.  
  122. Point3(int x, int y) {
  123. super();
  124. this.x = x;
  125. this.y = y;
  126. }
  127.  
  128. @Override
  129. public boolean equals(Object obj) {
  130. if (this == obj) return true;
  131. if (obj == null) return false;
  132. if (getClass() != obj.getClass()) return false;
  133. Point3 other = (Point3) obj;
  134. if (x != other.x) return false;
  135. if (y != other.y) return false;
  136. return true;
  137. }
  138.  
  139. public int getX() {
  140. return x;
  141. }
  142.  
  143. public void setX(int x) {
  144. this.x = x;
  145. }
  146.  
  147. public int getY() {
  148. return y;
  149. }
  150.  
  151. public void setY(int y) {
  152. this.y = y;
  153. }
  154. }

对于自描述方法toString(),系统默认的方法如下:

  1. public String toString() {
  2. return getClass().getName() + "@" + Integer.toHexString(hashCode());
  3. }

这样得到的并非自描述信息,而是实例的内存地址。所以,针对上面的Point1类,可以给出下面的自描述方法:

  1. public String toString() {
  2. return getClass().getName() + "@[x=" + this.getX() + ", y=" + this.getY() + "]";
  3. }

参考内容:

重新编写equals()方法,hashCode()方法,以及toString(),提供自定义的相等标准,以及自描述方法的更多相关文章

  1. 如何编写出高质量的 equals 和 hashcode 方法?

    什么是 equals 和 hashcode 方法? 这要从 Object 类开始说起,我们知道 Object 类是 Java 的超类,每个类都直接或者间接的继承了 Object 类,在 Object ...

  2. 帮助新手理解equals和hashCode

    入行快要两年,偶尔想起来equals和hash还是会有些晕,索性今天就更深入的弄明白一些,不足之处也请各位大神指出批评,共同进步. 刚开始学java的时候只是记忆性的来背,如果一个类在程序中可能进行比 ...

  3. Java基础(六)判断两个对象相等:equals、hashcode、toString方法

    1.equal方法 Object类中的equal方法用于检测一个对象是否等于另外一个对象.在Object类中,这个方法将判断两个对象是否具有相同的引用.如果两个对象具有相同的引用,它们一定是相等的.然 ...

  4. 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)

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

  5. 复写equals、hashCode和toString方法

    equals.hashCode和toString 这三个方法都是object类的方法,由于所有的类都是继承这个类,所以每一个类都有这三个方法. 1.复写equals方法 原则: 首先,两个实例是相同的 ...

  6. Java:验证在类继承过程中equals()、 hashcode()、toString()方法的使用

    以下通过实际例子对类创建过程汇中常用的equals().hashcode().toString()方法进行展示,三个方法的创建过程具有通用性,在项目中可直接改写. //通过超类Employee和其子类 ...

  7. Java提高篇——equals()与hashCode()方法详解

    java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继 ...

  8. java基础(十六)----- equals()与hashCode()方法详解 —— 面试必问

    本文将详解 equals()与hashCode()方法 概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) publi ...

  9. Java基础:Object类中的equals与hashCode方法

    前言 这个系列的文章主要用来记录我在学习和复习Java基础知识的过程中遇到的一些有趣好玩的知识点,希望大家也喜欢. 一切皆对象   对于软件工程来说面向对象编程有一套完整的解决方案:OOA.OOD.O ...

随机推荐

  1. Javascript 你不知道的事

    NaN表示一个不能产生正常结果的运算结果.它不等于任何值,包括它自己.可以用isNaN(number)来检测. 同Java中的字符串一样,JS中的字符串是不可变的.也就是说一旦字符串被创建,就无法改变 ...

  2. [Java] System.arraycopy 数组复制

    函数原型: public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) ; s ...

  3. scanner, BufferedReader, InputStreamReader 区别及特殊字符的输入

    1. Scanner是一个可以使用正则表达式来分析基本类型和字符串的简单文本扫描器!也就是控制台应用程序最为常用的文本输入方式!Scanner取得输入数据的依据是空格符:如按下空格键,Tab键或者En ...

  4. 【UVa】Partitioning by Palindromes(dp)

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=27&page=sh ...

  5. mac zsh选择到行首的快捷键

    Mac OS X 下zsh切换窗口的快捷键:Shift-Command-←. 移动到当前命令行的行首,使用快捷键[Ctrl][A].移动到当前命令行的行尾,使用快捷键[Ctrl[E].

  6. RabbitMQ之Queues-5

    工作队列的主要任务是:避免立刻执行资源密集型任务,然后必须等待其完成.相反地,我们进行任务调度:我们把任务封装为消息发送给队列.工作进行在后台运行并不断的从队列中取出任务然后执行.当你运行了多个工作进 ...

  7. Oracle dbms_random随机数包详解

    Oracle dbms_random包主要用于获得随机数,可以为数字也可以为字母等,还可以实现混拼.常用函数如下: dbms_random.value 生成一个指定范围的38位随机小数(小数点后38位 ...

  8. 显示图片中CDC和HDC问题

    (m_Pic.LoadPictureData(pBuffer, nSize));//接作调用函数读pBuffer的jpg数据准备显示 showimage();//显示图片 void Caccess_t ...

  9. Lumen Carbon 日期及时间处理包

    用到过的方法: 获取当前Y-m-d H:i:s Carbon::now()->toDateTimeString() 把 Y-m-d H:i:s 转 Y-m-d Carbon::parse('Y- ...

  10. 剑指 offer set 13 把数组排成最小的数

    总结 1. 给定 3, 32, 321 将他们组合成最小的数, 比如 321323 2. 3    ->   333 32   ->   322 321 ->   321 然后再排序