equals方法

不覆写equals时候

equals() 的作用是 用来判断两个对象是否相等。


equals() 定义在JDK的Object.java中。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:


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



既然Object.java中定义了equals()方法,这就意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。 但是,我们已经说过,使用默认的“equals()”方法,等价于“==”方法。


因此,我们通常会重写equals()方法:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。  


下面根据“类是否覆盖equals()方法”,将它分为2类。


(01) 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。


(02) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。


[java]

1. package org.senssic;  
2.   
3. public class Seh {  
4. private  String name;  
5. private  int age;  
6.   
7. public Seh(String name, int age) {  
8. this.age = age;  
9. this.name = name;  
10.     }  
11.   
12. public static void main(String[] args) {  
13.   
14. new Seh("senssic", 20);  
15. new Seh("sensisc", 20);  
16.         Seh seh3 = seh;  
17. // 没有覆写equals方法下,equals方法和==方法一样都是通过比较地址比较两个对象  
18. "--->" + (seh == seh2));  
19. // 只能通过比较地址来判断是否同一对象  
20. "--->" + (seh == seh3) + "--->"  
21.                 + (seh2 == seh3));  
22.     }  
23. }


结果:

false--->false
true--->true--->false

因为没有覆写equals方法,所以即便对象中内容一样也是通过判断地址来比较对象的。

覆写equals时候

覆写equals方法的步骤
1.比较地址,如果地址相同,肯定是同一对象
2.如果比较的对象为null直接返回false
3.比较类的字节码
4.向下类型转换比较属性
5.比较属性
如果是基本属性(除去浮点数)直接判断是否相等
如果是double类型通过doubleToLongBits方法转换为long类型再比较
如果是float类型通过floatToIntBits方法转换为int类型再比较
如果是引用对象先判断是否为空,再调用其对应的equals方法


[java] 

1. package org.senssic;  
2.   
3. public class Seh {  
4. private  String name;  
5. private  int age;  
6. private byte byt;  
7. private short shot;  
8. private long lo;  
9. private char ch;  
10. private boolean bool;  
11. private float fl;  
12. private double dou;  
13.   
14. @Override  
15. public boolean equals(Object obj) {  
16. if (this == obj)  
17. return true;  
18. if (obj == null)  
19. return false;  
20. if (getClass() != obj.getClass())  
21. return false;  
22.         Seh other = (Seh) obj;  
23. if (age != other.age)  
24. return false;  
25. if (bool != other.bool)  
26. return false;  
27. if (byt != other.byt)  
28. return false;  
29. if (ch != other.ch)  
30. return false;  
31. if (Double.doubleToLongBits(dou) != Double.doubleToLongBits(other.dou))  
32. return false;  
33. if (Float.floatToIntBits(fl) != Float.floatToIntBits(other.fl))  
34. return false;  
35. if (lo != other.lo)  
36. return false;  
37. if (name == null) {  
38. if (other.name != null)  
39. return false;  
40. else if (!name.equals(other.name))  
41. return false;  
42. if (shot != other.shot)  
43. return false;  
44. return true;  
45.     }  
46.   
47. public Seh(String name, int age) {  
48. this.age = age;  
49. this.name = name;  
50.     }  
51.   
52. public static void main(String[] args) {  
53.   
54. new Seh("senssic", 20);  
55. new Seh("senssic", 20);  
56.         Seh seh3 = seh;  
57. // 覆写equals方法下,equals方法和==方法一样不一样equals比较同一对象,==比较地址  
58. "--->" + (seh == seh2));  
59. // 只能通过equals方法来比较是否同一对象  
60. "--->" + (seh == seh3) + "--->"  
61.                 + (seh2 == seh3));  
62.     }  
63. }

结果:

true--->false
true--->true--->false
覆写了equals方法后如果两个对象的属性都相等,则返回true

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() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:


                 情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。


                 情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。


hashCode() 方法

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
       虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”(关于“散列表”见下面说明)时,
       该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),
       类的hashCode() 没有作用。
       上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。
       也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
比如HashSet是Set集合,为了快速的定位里面的元素,就需要用到散列码确定,然后进行操作。当然如果发现其对应的hash表中有相同的hash值,只保存一个。

set判断不同对象的顺序:1>通过hashCode()的值快速比较,如果hash值不相等,判定他们不同;
                                             2>如果hashCode相等,就比较equals()
                                              如果equals()相等,就判定这两个对象相等,否则不相等


若两个元素相等,它们的散列码一定相等;但反过来确不一定。在散列表中,

 1、如果两个对象相等,那么它们的hashCode()值一定要相同;

 2、如果两个对象hashCode()相等,它们并不一定相等。

[java]

1. package org.senssic;  
2.   
3. public class Seh {  
4. private final String name;  
5. private final int age;  
6. private byte byt;  
7. private short shot;  
8. private long lo;  
9. private char ch;  
10. private boolean bool;  
11. private float fl;  
12. private double dou;  
13.   
14. // Objec中的hashCode()方法是native方法,由本地代码自动生成,如果不覆写hashCode则由native代码生成一个hash值  
15. @Override  
16. public int hashCode() {  
17. // 之所以选择31,是因为它是个奇素数,如果乘数是偶数,并且乘法溢出的话,  
18. // 信息就会丢失,因为与2相乘等价于移位运算。使用素数的好处并不是很明显  
19. // ,但是习惯上都使用素数来计算散列结果。31有个很好的特性,就是用移位和减法来代替乘法,  
20. // 可以得到更好的性能:31*i==(i<<5)-i。现在的VM可以自动 //完成这种优化。  
21. final int prime = 31;  
22. int result = 1;  
23.         result = prime * result + age;  
24. 1231 : 1237);// 1231 1237都是素数  
25.         result = prime * result + byt;  
26.         result = prime * result + ch;  
27. long temp;  
28.         temp = Double.doubleToLongBits(dou);  
29. int) (temp ^ (temp >>> 32));  
30.         result = prime * result + Float.floatToIntBits(fl);  
31. int) (lo ^ (lo >>> 32));  
32. null) ? 0 : name.hashCode());  
33.         result = prime * result + shot;  
34. return result;  
35.     }  
36.   
37. public Seh(String name, int age) {  
38. this.age = age;  
39. this.name = name;  
40.     }  
41.   
42. public static void main(String[] args) {  
43.   
44. new Seh("senssic", 21);  
45. new Seh("senssic", 21);  
46. // 虽然两个对象的hash值相等但此对象并不相等,所以:对象相等hash值一定相同,hash值相等对象不一定相等  
47. "--->" + seh2.hashCode());  
48.     }  
49. }


集合类型排序


Java API针对集合类型排序提供了两种支持:


第一个方法要求所排序的元素类必须实现java.lang.Comparable接口。
第二个方法要求实现一个java.util.Comparator接口,需要写个额外的排序类还指定排序方法。


1.实现Comparable接口

[java]

1. package org.senssic;  
2.   
3. import java.util.ArrayList;  
4. import java.util.Collections;  
5. import java.util.List;  
6. import java.util.Map;  
7. import java.util.Map.Entry;  
8. import java.util.Set;  
9. import java.util.TreeMap;  
10. import java.util.TreeSet;  
11.   
12. public class Person implements Comparable<Person> {  
13. private  String name;  
14. private  int age;  
15. private  int score;  
16. private  int english;  
17.   
18. public Person(String name, int age, int score, int english) {  
19. this.name = name;  
20. this.age = age;  
21. this.score = score;  
22. this.english = english;  
23.     }  
24.   
25. @Override  
26. public int hashCode() {  
27. final int prime = 31;  
28. int result = 1;  
29.         result = prime * result + age;  
30.         result = prime * result + english;  
31. null) ? 0 : name.hashCode());  
32.         result = prime * result + score;  
33. return result;  
34.     }  
35.   
36. @Override  
37. public boolean equals(Object obj) {  
38. if (this == obj)  
39. return true;  
40. if (obj == null)  
41. return false;  
42. if (getClass() != obj.getClass())  
43. return false;  
44.         Person other = (Person) obj;  
45. if (age != other.age)  
46. return false;  
47. if (english != other.english)  
48. return false;  
49. if (name == null) {  
50. if (other.name != null)  
51. return false;  
52. else if (!name.equals(other.name))  
53. return false;  
54. if (score != other.score)  
55. return false;  
56. return true;  
57.     }  
58.   
59. // 1表示大于0表示等于-1表示小于  
60. @Override  
61. public int compareTo(Person p) {  
62. if (this.age > p.age) {// 如果年龄大于直接排序按年龄  
63. return -1;  
64. else if (this.score > p.score) {// 如果年龄小于或等于再比较分数按分数高低排  
65. return 1;  
66. else {// 如果年龄小于且分数小于等于按英语成绩排  
67. if (this.english > p.english) {  
68. return 1;  
69. else if (this.english < p.english) {  
70. return -1;  
71. else {  
72. return 0;  
73.             }  
74.   
75.         }  
76.     }  
77.   
78. @Override  
79. public String toString() {  
80. // TODO Auto-generated method stub  
81. return "名字:" + this.name;  
82.     }  
83.   
84. public static void main(String[] args) {  
85. new Person("senssic", 12, 23, 45);  
86. new Person("qiyu", 12, 23, 41);  
87. new Person("zhangsan", 12, 21, 41);  
88. new Person("zhaosi", 13, 21, 41);  
89. // 使用list  
90. new ArrayList<>();  
91.         list.add(param);  
92.         list.add(pers);  
93.         list.add(perso);  
94.         list.add(person);  
95. // 集合工具类排序list  
96. for (Person per : list) {  
97.             System.out.println(per.toString());  
98.         }  
99. // 使用set  
100. new TreeSet<>();// 因为treeSet是唯一实现SortedSet接口,可以自动实现排序,但是效率低些  
101.         set.add(param);  
102.         set.add(pers);  
103.         set.add(perso);  
104.         set.add(person);  
105. "--->" + perso.equals(param)  
106.                 + perso.equals(pers) + perso.equals(person));  
107. for (Person p : set) {  
108.             System.out.println(p.toString());  
109. // 此处只有三个输出因为:TreeSet判断两个对象不相等的标准是:两个对象通过equals方法比较返回false,  
110. // 或通过compareTo(Object  
111. // obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理。  
112. // 因为上面的param和perso比较时候没有返回0所以认为是同一个对象,如果使用hashSet就会有四个输出。  
113.         }  
114. // 使用map  
115. new TreeMap<Person, String>();// 通过二叉树算法,可以自动实现排序,但是效率低些  
116. "中国");  
117. "安徽");  
118. "池州");  
119. "阜阳");  
120. for (Entry<Person, String> mapEntry : map.entrySet()) {  
121.             System.out  
122. "--->" + mapEntry.getValue());  
123. // 此处输出的也为三个,比较对象相同方法同TreeSet一样  
124.         }  
125.     }  
126. }



2.实现Comparator接口

[java] 

1. package org.senssic;  
2.   
3. import java.util.ArrayList;  
4. import java.util.Collections;  
5. import java.util.Comparator;  
6. import java.util.List;  
7. import java.util.Map;  
8. import java.util.Map.Entry;  
9. import java.util.Set;  
10. import java.util.TreeMap;  
11. import java.util.TreeSet;  
12.   
13. class PersonComper implements Comparator<Person> {  
14.   
15. @Override  
16. public int compare(Person p, Person per) {  
17. if (p.getAge() > per.getAge()) {// 如果年龄大于直接排序按年龄  
18. return -1;  
19. else if (p.getScore() > per.getScore()) {// 如果年龄小于或等于再比较分数按分数高低排  
20. return 1;  
21. else {// 如果年龄小于且分数小于等于按英语成绩排  
22. if (p.getEnglish() > per.getEnglish()) {  
23. return 1;  
24. else if (p.getEnglish() < per.getEnglish()) {  
25. return -1;  
26. else {  
27. return 0;  
28.             }  
29.   
30.         }  
31.     }  
32. }  
33.   
34. public class Person {  
35. private String name;  
36. private int age;  
37. private int score;  
38. private int english;  
39.   
40. public String getName() {  
41. return name;  
42.     }  
43.   
44. public void setName(String name) {  
45. this.name = name;  
46.     }  
47.   
48. public int getAge() {  
49. return age;  
50.     }  
51.   
52. public void setAge(int age) {  
53. this.age = age;  
54.     }  
55.   
56. public int getScore() {  
57. return score;  
58.     }  
59.   
60. public void setScore(int score) {  
61. this.score = score;  
62.     }  
63.   
64. public int getEnglish() {  
65. return english;  
66.     }  
67.   
68. public void setEnglish(int english) {  
69. this.english = english;  
70.     }  
71.   
72. public Person(String name, int age, int score, int english) {  
73. this.name = name;  
74. this.age = age;  
75. this.score = score;  
76. this.english = english;  
77.     }  
78.   
79. @Override  
80. public int hashCode() {  
81. final int prime = 31;  
82. int result = 1;  
83.         result = prime * result + age;  
84.         result = prime * result + english;  
85. null) ? 0 : name.hashCode());  
86.         result = prime * result + score;  
87. return result;  
88.     }  
89.   
90. @Override  
91. public boolean equals(Object obj) {  
92. if (this == obj)  
93. return true;  
94. if (obj == null)  
95. return false;  
96. if (getClass() != obj.getClass())  
97. return false;  
98.         Person other = (Person) obj;  
99. if (age != other.age)  
100. return false;  
101. if (english != other.english)  
102. return false;  
103. if (name == null) {  
104. if (other.name != null)  
105. return false;  
106. else if (!name.equals(other.name))  
107. return false;  
108. if (score != other.score)  
109. return false;  
110. return true;  
111.     }  
112.   
113. @Override  
114. public String toString() {  
115. // TODO Auto-generated method stub  
116. return "名字:" + this.name;  
117.     }  
118.   
119. public static void main(String[] args) {  
120. new Person("senssic", 12, 23, 45);  
121. new Person("qiyu", 12, 23, 41);  
122. new Person("zhangsan", 12, 21, 41);  
123. new Person("zhaosi", 13, 21, 41);  
124. // 使用list  
125. new ArrayList<>();  
126.         list.add(param);  
127.         list.add(pers);  
128.         list.add(perso);  
129.         list.add(person);  
130. new PersonComper();  
131. // 集合工具类排序list  
132. for (Person per : list) {  
133.             System.out.println(per.toString());  
134.         }  
135. // 使用set  
136. new TreeSet<>(pComper);// 使用TreeSet的构造方法传入Comparator的接口实现,因为treeSet是唯一实现SortedSet接口,可以自动实现排序,但是效率低些  
137.         set.add(param);  
138.         set.add(pers);  
139.         set.add(perso);  
140.         set.add(person);  
141. "--->" + perso.equals(param)  
142.                 + perso.equals(pers) + perso.equals(person));  
143. for (Person p : set) {  
144.             System.out.println(p.toString());  
145. // 此处只有三个输出因为:TreeSet判断两个对象不相等的标准是:两个对象通过equals方法比较返回false,  
146. // 或通过compareTo(Object  
147. // obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理。  
148. // 因为上面的param和perso比较时候没有返回0所以认为是同一个对象,如果使用hashSet就会有四个输出。  
149.         }  
150. // 使用 map  
151. new TreeMap<>(pComper);// 使用TreeSet的构造方法传入Comparator的接口实现,可以自动实现排序,但是效率低些  
152. "中国");  
153. "安徽");  
154. "池州");  
155. "阜阳");  
156. for (Entry<Person, String> mapenEntry : map.entrySet()) {  
157. "--->"  
158.                     + mapenEntry.getValue());  
159. // 此处输出的也为三个,比较对象相同方法同TreeSet一样  
160.         }  
161.   
162.     }  
163. }  
164.