Java中一个实体类重写了equals方法,但没有重写hashCode方法,会有什么问题。
首先,说下equals和hashCode的关系。JDK API中关于Object类的equals和hashCode方法中说过,总结起来就是两句话:equals相等的两个对象的hashCode也一定相等,但hashCode相等的两个对象不一定equals相等。
hashCode类似于一个位置值(不叫地址值,是想把每个对象所在的位置做地址值),HashSet、HashMap等集合类中常会用到。
上图中假设是对象在内存中的模型,则7—c就是位置值即hashCode,而71—74就是地址值。所以x,y和z的hashCode是一样的,但是x、y和z的equals不一定相等,equals相等只跟自己类定义的equals方法有关,加设备x是A类的对象,y类是B类对象,z是C类对象,A、B和C的equals都实现为始终返回true,则程序会认为x、y和z是相等的,若都实现为返回false,则x跟自己都不相等。即equals相等依据的是equals方法。
当我们重写equals方法时,是有要求的(具体见JDK)。如果只重写了equals,不重写hashCode会有什么影响呢?
假如该类不会放在HashSet等散列表相关的集合类中,不一定会有影响,如下代码:
Model类
1 package com.guanmu.test;
2
3
4 /**
5 * <p>
6 * 类描述:
7 * <p>
8 *
9 * 所属插件:com.guanmu.test
10 *
11 * @author guanmu 2015-10-20
12 *
13 */
14 public class Model {
15
16 private String name;
17 private String age;
18 private String otherName;
19
20 /**
21 * @param name
22 * @param age
23 * @param otherName
24 */
25 public Model(String name, String age, String otherName) {
26 super();
27 this.name = name;
28 this.age = age;
29 this.otherName = otherName;
30 }
31
32 /**
33 * @return the name
34 */
35 public String getName() {
36 return name;
37 }
38
39 /**
40 * @param name
41 * the name to set
42 */
43 public void setName(String name) {
44 this.name = name;
45 }
46
47 /**
48 * @return the age
49 */
50 public String getAge() {
51 return age;
52 }
53
54 /**
55 * @param age
56 * the age to set
57 */
58 public void setAge(String age) {
59 this.age = age;
60 }
61
62 /**
63 * @return the otherName
64 */
65 public String getOtherName() {
66 return otherName;
67 }
68
69 /**
70 * @param otherName
71 * the otherName to set
72 */
73 public void setOtherName(String otherName) {
74 this.otherName = otherName;
75 }
76
77 @Override
78 public int hashCode() {
79
80 int a = 7;
81 int b = 11;
82 // a和b为不相等的int型常量
83 int r = a;
84 r = r*b + name.hashCode();
85 r = r*b + age.hashCode();
86
87 // return r;
88 return super.hashCode();
89 }
90
91 @Override
92 public boolean equals(Object obj) {
93 if (!(obj instanceof Model)) {
94 return false;
95 }
96
97 Model other = (Model) obj;
98 if (name.equals(other.getName()) && age.equals(other.getAge())) {
99 return true;
100 }
101
102 return false;
103 }
104
105 /* (non-Javadoc)
106 * @see java.lang.Object#toString()
107 */
108 @Override
109 public String toString() {
110 return "Model [name=" + name + ", age=" + age + ", otherName=" + otherName + "]";
111 }
112
113
114 }
View Code
测试类
1 package com.guanmu.test;
2
3 import java.util.ArrayList;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Set;
7
8 /**
9 * <p>
10 * 类描述:
11 * <p>
12 *
13 * 所属插件:com.guanmu.test
14 * @author guanmu2015-10-20
15 *
16 */
17 public class EqualsTest {
18
19
20
21 public static void main(String[] args) {
22 test1();
23 test2();
24 }
25
26 public static void test1() {
27 System.out.println("####:test1");
28 List<Model> list = new ArrayList<Model>();
29
30 Model a = new Model("a", "20", "test");
31 Model b = new Model("a","20","abcdef");
32 Model c = new Model("a","20","");
33
34 list.add(a);
35 System.out.println("a-hashCode:" + a.hashCode());
36 System.out.println(list);
37 if (!list.contains(b)) {
38 list.add(b);
39 System.out.println("b-hc:" + b.hashCode());
40 System.out.println(list);
41 }
42
43 }
44
45 public static void test2() {
46 System.out.println("####:test2");
47 Set<Model> set = new HashSet<Model>();
48
49 Model a = new Model("a", "20", "test");
50 Model b = new Model("a","20","abcdef");
51 Model c = new Model("a","20","");
52
53 set.add(a);
54 System.out.println("a-hashCode:" + a.hashCode());
55 System.out.println(set);
56 if (!set.contains(b)) {
57 set.add(b);
58 System.out.println("b-hc:" + b.hashCode());
59 System.out.println(set);
60 }
61
62 }
63 }
View Code
结果打印如下:
####:test1
a-hashCode:18923308
[Model [name=a, age=20, otherName=test]]
####:test2
a-hashCode:15136722
[Model [name=a, age=20, otherName=test]]
b-hc:26752749
[Model [name=a, age=20, otherName=test], Model [name=a, age=20, otherName=abcdef]]
list中能判断出包含了等价的b,但set中认为a和b不相等,set中没有包含b。发现test1()中,ArrayList是根据equals来判断是后包含,而不管hashCode是否不相等。而test2()中,HashSet处理流程则不一样,先判断两个对象的hashCode方法是否一样,如果不一样,立即认为两个对象equals不相等,并不调用equals方法,当hashCode相等时,再根据equals方法判断两个对象是否相等。
总结,所以当我们缩写的类可能用于存放在Hash相关的集合类中时,在重写equals时,需要重写hashCode,不然会出现与预期不符的结果。从网上搜索了下如何重写hashCode方法,以下据说是《Think in Java》中提到的方法。
@Override
public int hashCode() {
int a = 7;
int b = 11;
// a和b为不相等的int型常量
int r = a;
r = r*b + name.hashCode();
r = r*b + age.hashCode();
return r;
}
其中a和b可以为任意不相等常量。
每多学一点知识,就少写一行代码。