hashcode和equals方法的区别和联系

说到 hashcode就要和Java中的集合,HashSet,HashMap 关系最为密切。

首先附录两张Java的集合结构图:

图二:(上图的简化版)

从Set集合的特点说起 & Set是如何去除重复元素的:

Set:元素不可以重复,是无序的。 Set接口中的方法和Collection一致(看上面的集合框架图)。

  |--HashSet: 内部数据结构是哈希表 ,是不同步的。

如何保证该集合的元素唯一性呢?

是通过对象的hashCode和equals方法来完成对象唯一性的。
hashCode值和对象的内容是存储在hash表中的。 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。 如果为true,视为相同元素,不存储。如果为false,那么视为不同元素,就进行存储

记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,比如人(Person类),学生(Student类),书(Book类),通常都需要覆盖equalshashCode方法。

建立对象判断是否相同的依据。

 

哈希算法是底层windows做的,而且针对不同的对象有不同的哈希算法运算。

java类中有的哈希算法是native用的windows底层 的,有的根据对象的不同进行了重写。

哈希表:哈希这种算法会算出很多的值,把这些值存储起来形成一个表。表中有对应关系。

哈希表的结构还是数组。只是哈希这种算法对数组进行了优化。

哈希算法有一种最常见的算法就是取余。

哈希表中不能有重复的元素。

哈希表是如何判断元素是否是重复的? 举例一种取余的HashCode算法原理:

 

ab”和”ba”产生的哈希值是一样的,取余之后都是5,但是内容不一样,哈希值对这样的情况也有对应的方法,在5角标出“挂”一个”ba”。

list集合removecontains都要判断相不相同,都是用的equals,不用hashcode

如果到了Set集合,要删除一个元素,要依据该元素是否和该容器中的元素是否相同。HashSet依据对象的hashcodeequals

代码举例,Set中存储自定义的类对象:

 ①原始的方式,不重写equals也不重写hashcode。

 Person.java

 1 public class Person {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person(String name, int age) {
17         super();
18         this.name = name;
19         this.age = age;
20     }
21 }

CollectionTest.java

 1 import java.util.ArrayList;
 2 import java.util.HashMap;
 3 import java.util.HashSet;
 4 import java.util.Iterator;
 5 import java.util.Map;
 6 import java.util.Map.Entry;
 7 import java.util.Set;
 8 public class CollectionTest {
 9     public static void main(String[] args) {
10         //单个对象
11         Person p1 = new Person("kxh",22);
12         Person p2 = new Person("kxh",22);
13         System.out.println("p1 == p2:" + (p1 == p2));
14         System.out.println("p1.equals(p2):" + p1.equals(p2));
15         //List集合
16         ArrayList<Person> arrayList = new ArrayList<Person>();
17         arrayList.add(new Person("lisi1",11));
18         arrayList.add(new Person("lisi2",12));
19         arrayList.add(new Person("lisi3",13));
20         arrayList.add(new Person("lisi4",14));
21         arrayList.add(new Person("lisi4",14));
22         //HashMap集合
23         HashMap<Person,Object> hashMap = new HashMap<Person,Object>();
24         hashMap.put(new Person("lisi1",11),new Object());
25         hashMap.put(new Person("lisi2",12),new Object());
26         hashMap.put(new Person("lisi3",13),new Object());
27         hashMap.put(new Person("lisi4",14),new Object());
28         hashMap.put(new Person("lisi4",14),new Object());
29         //HashSet集合
30         HashSet<Person> hashSet = new HashSet<Person> ();
31         hashSet.add(new Person("lisi1",11));
32         hashSet.add(new Person("lisi2",12));
33         hashSet.add(new Person("lisi3",13));
34         hashSet.add(new Person("lisi4",14));
35         hashSet.add(new Person("lisi4",14));
36 
37         //遍历ArrayList集合
38         System.out.println("遍历ArrayList集合------");
39         Iterator arrayListIterator = arrayList.iterator();
40         while(arrayListIterator.hasNext()){
41             Person p = (Person) arrayListIterator.next();
42             System.out.println(p.getName()+"---"+p.getAge());
43         }
44         
45         //遍历HashMap集合
46         System.out.println("遍历HashMap集合------");
47         Set<Entry<Person, Object>> entrySet = hashMap.entrySet();
48         Iterator entrySetIterator = entrySet.iterator();
49         while(entrySetIterator.hasNext()){
50             Map.Entry entry = (Map.Entry)entrySetIterator.next();
51             System.out.println(entry.getKey()+"---"+entry.getValue());
52         }
53         
54         //遍历HashSet集合
55         System.out.println("遍历HashSet集合------");
56         Iterator hashSetIterator = hashSet.iterator();
57         while(hashSetIterator.hasNext()){
58             Person p = (Person) hashSetIterator.next();
59             System.out.println(p.getName()+"---"+p.getAge());
60         }
61     }
62 }

 程序输出:

p1 == p2:false
p1.equals(p2):false
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
cn.cdv.collection.Person@52e922---java.lang.Object@1b84c92
cn.cdv.collection.Person@10dea4e---java.lang.Object@1c7c054
cn.cdv.collection.Person@1db9742---java.lang.Object@12204a1
cn.cdv.collection.Person@106d69c---java.lang.Object@a298b7
cn.cdv.collection.Person@25154f---java.lang.Object@14991ad
遍历HashSet集合------
lisi1---11
lisi2---12
lisi4---14
lisi4---14
lisi3---13

ArrayList中可以存储重复元素,HashMap中使用了两个“一样的”new Person("lisi4",14)作为key,同一个key在HashMap中只能有一个,HashSet中存储进去了两个lisi4 14”又是集合Set Set集合中不会再存储相同的元素。为什么出现这种情况。

  ①这是Map和Set存储用户自定义对象。

  ②Person类继承的是Objectnew一个Person都有不同的地址值,hashcode不同,对于Set集合来说是不同的元素,对Map来说是不同的Key。

  ③你自己定义的只要名字和年龄一样就是不同的对象这个规则程序现在是不知道的。需要你自己去定义,完善。

②只重写equals方法,在equals方法中判断Person对象的name和age属性值是否相等来判断对象是否相同。

 1 public class Person extends Object {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person(String name, int age) {
17         super();
18         this.name = name;
19         this.age = age;
20     }
21     
22     @Override
23     public boolean equals(Object obj) {
24         if(this == obj){//当判断完hashCode()方法之后hah值相同,如果内容也相同,使用==这样结束equals语句中的判断。
25             return true;
26         }
27         if(!(obj instanceof Person)){
28             throw new ClassCastException("类型错误");
29         }
30         //运行到这里说明两者 相等,打印输出。。。
31         System.out.println(this+"....重写equals判断相同....."+obj);
32         Person p = (Person)obj;
33         return this.name.equals(p.name) && this.age == p.age;
34     }
35     
36     public String toString(){
37         return name +":"+ age;
38     }
39 }

 程序输出:

p1 == p2:false
kxh:22....重写equals判断相同.....kxh:22
p1.equals(p2):true
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi3:13---java.lang.Object@1b84c92
lisi4:14---java.lang.Object@1c7c054
lisi1:11---java.lang.Object@12204a1
lisi2:12---java.lang.Object@a298b7
lisi4:14---java.lang.Object@14991ad
遍历HashSet集合------
lisi1---11
lisi2---12
lisi4---14
lisi4---14
lisi3---13

结果分析:因为Person两个对象的age和name属性相等,而且又是通过覆盖equals方法来判断的,所示p1.equals(p2) 为true。

只重写equals方法,加入ArrayList,HashMap和HashSet的元素都全部打印出来。

③通过以上代码我们知道equals方法已经生效。接下来我们在覆盖一下hashCode方法(通过age和name属性来生成hashcode)并不覆盖equals方法,其中Hash码是通过age和name生成的。

 1 public class Person extends Object {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person(String name, int age) {
17         super();
18         this.name = name;
19         this.age = age;
20     }
21     
22     /*
23      * hashCode()方法返回的是一个int类型的值,这里根据Person类对象的特点,
24      * 返回一个由name和age属性决定的值最合适。age之后乘以的数字只要不是1,都可以。
25      */
26     @Override
27     public int hashCode() {
28         System.out.println(this+".......hashCode的值:" + name.hashCode()+age*27);
29         return name.hashCode()+age*27;
30     }
31     
32     public String toString(){
33         return name +":"+ age;
34     }
35 }

 测试类不变,运行测试类,程序输出:

p1 == p2:false
p1.equals(p2):false
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi2:12---java.lang.Object@1db9742
lisi1:11---java.lang.Object@106d69c
lisi4:14---java.lang.Object@52e922
lisi4:14---java.lang.Object@25154f
lisi3:13---java.lang.Object@10dea4e
遍历HashSet集合------
lisi2---12
lisi1---11
lisi4---14
lisi4---14
lisi3---13

上面ArrayList,HashMap,HashSet总共三组,但是只打印了两组数据的hashcode。由集合的特性可知,这两组是HashMap和HashSet添加数据时打印的,ArrayList中添加数据是不会判断hashcode值的。

我们并没有覆盖equals方法只覆盖了hashCode方法,对于HashMap两个对象中有两个new Person("lisi4",14)作为key,对于HashSet中放入有两个new Person("lisi4",14),

虽然两个对象new Person("lisi4",14)的hashCode一样,但是根据比较原则,先比较hashcode,如果hashcode相同再比较equals,很明显两个new Person("lisi4",14) 对应的值为false,所以加入到HashMap和HashSet的值全部打印出来。

 ④既覆盖hashCode() 又重写equals()方法

 1 public class Person extends Object {
 2     private String name;
 3     private int age;
 4     public String getName() {
 5         return name;
 6     }
 7     public void setName(String name) {
 8         this.name = name;
 9     }
10     public int getAge() {
11         return age;
12     }
13     public void setAge(int age) {
14         this.age = age;
15     }
16     public Person(String name, int age) {
17         super();
18         this.name = name;
19         this.age = age;
20     }
21     
22     /*
23      * hashCode()方法返回的是一个int类型的值,这里根据Person类对象的特点,
24      * 返回一个由name和age属性决定的值最合适。age之后乘以的数字只要不是1,都可以。
25      */
26     @Override
27     public int hashCode() {
28         System.out.println(this+".......hashCode的值:" + name.hashCode()+age*27);
29         return name.hashCode()+age*27;
30     }
31 
32     @Override
33     public boolean equals(Object obj) {
34         if(this == obj){//当判断完hashCode()方法之后hah值相同,如果内容也相同,使用==这样结束equals语句中的判断。
35             return true;
36         }
37         if(!(obj instanceof Person)){
38             throw new ClassCastException("类型错误");
39         }
40         //运行到这里说明两者 相等,打印输出。。。
41         System.out.println(this+"....重写equals判断相同....."+obj);
42         Person p = (Person)obj;
43         return this.name.equals(p.name) && this.age == p.age;
44     }
45     
46     public String toString(){
47         return name +":"+ age;
48     }
49 }

 测试类不变,运行输出:

p1 == p2:false
kxh:22....重写equals判断相同.....kxh:22
p1.equals(p2):true
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi4:14....重写equals判断相同.....lisi4:14
lisi1:11.......hashCode的值:102982142297
lisi2:12.......hashCode的值:102982143324
lisi3:13.......hashCode的值:102982144351
lisi4:14.......hashCode的值:102982145378
lisi4:14.......hashCode的值:102982145378
lisi4:14....重写equals判断相同.....lisi4:14
遍历ArrayList集合------
lisi1---11
lisi2---12
lisi3---13
lisi4---14
lisi4---14
遍历HashMap集合------
lisi2:12---java.lang.Object@1db9742
lisi1:11---java.lang.Object@106d69c
lisi4:14---java.lang.Object@52e922
lisi3:13---java.lang.Object@25154f
遍历HashSet集合------
lisi2---12
lisi1---11
lisi4---14
lisi3---13

 通过结果可以看出,hashcode和equals都复写之后才真正保证了唯一性,复写equals方法必须复写hashcode方法。

附录另外有价值的博客:

http://blog.csdn.net/michaellufhl/article/details/5833188

http://blog.csdn.net/jiangwei0910410003/article/details/22739953

http://www.cnblogs.com/DreamDrive/p/5431725.html

 http://www.cnblogs.com/lulipro/p/5628750.html

  

原文地址:https://www.cnblogs.com/DreamDrive/p/7505464.html