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

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

 

对于自描述方法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 }


 

参考内容:

 

 

 

posted @ 2013-12-11 16:28  bluepoint2009  阅读(1135)  评论(0编辑  收藏  举报