Java基础: HashSet 与 hashCode、equals

大家都说 Java 很简单,的确 Java 入门不难,但是要想深入了解 Java 那不是一朝一夕能够做到的!

学习 Java 最重要的一点是要学习其设计思想和设计理念,比如集合框架、IO框架的设计等。


通过一个实例谈谈 HashSet 与 hashCode、equals 的使用,以及在使用时的注意事项。


设计一个 Person 类,如下:

[java]  view plain copy print ?
  1. package mark.zhang;  
  2.   
  3. public class Person {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.   
  8.     public Person(String name, int age) {  
  9.         super();  
  10.         this.name = name;  
  11.         this.age = age;  
  12.     }  
  13.   
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.   
  18.     public int getAge() {  
  19.         return age;  
  20.     }  
  21.   
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.   
  26.     public void setAge(int age) {  
  27.         this.age = age;  
  28.     }  
  29.   
  30.     @Override  
  31.     public String toString() {  
  32.         return "age=" + age + ", name=" + name;  
  33.     }  
  34.   
  35. }  


这个类很简单,两个成员变量以及 set、get 方法,注意这里没有重写 equals、hashCode 方法。为了在打印的时候方便看出结果,重写 toString 方法。


测试类也照样很简单,如下:

[java]  view plain copy print ?
  1. public class TestPerson {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Set<Person> set = new HashSet<Person>();  
  5.         Person p1 = new Person("喜洋洋"25);  
  6.         Person p2 = new Person("懒洋洋"26);  
  7.         Person p3 = new Person("灰太郎"27);  
  8.         set.add(p1);  
  9.         set.add(p2);  
  10.         set.add(p3);  
  11.         System.out.println(set.size() + " 个动画人物!");  
  12.   
  13.         for (Person person : set) {  
  14.             System.out.println(person);  
  15.         }  
  16.     }  
  17. }  

输出结果,如下所示:

[java]  view plain copy print ?
  1. 3 个动画人物!  
  2. age=27, name=灰太郎  
  3. age=26, name=懒洋洋  
  4. age=25, name=喜洋洋  

ok,看懂上面的程序很简单,只要你不是初学 Java 的话!但是今天的主题不是只讨论这段代码的难易程度。

如果在代码中删除一个“人”,很简单,只需要调用 remove 方法即可,如下所示:

[java]  view plain copy print ?
  1. set.remove(p2);  


这个时候,我需要修改 Person 这个类,重写父类 Object 的两个方法,equals、hashCode,修改之后的代码:

[java]  view plain copy print ?
  1. package mark.zhang;  
  2.   
  3. public class Person {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.   
  8.     public Person(String name, int age) {  
  9.         super();  
  10.         this.name = name;  
  11.         this.age = age;  
  12.     }  
  13.   
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.   
  18.     public int getAge() {  
  19.         return age;  
  20.     }  
  21.   
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.   
  26.     public void setAge(int age) {  
  27.         this.age = age;  
  28.     }  
  29.   
  30.     @Override  
  31.     public String toString() {  
  32.         return "age=" + age + ", name=" + name;  
  33.     }  
  34.   
  35.     @Override  
  36.     public int hashCode() {  
  37.         final int prime = 31;  
  38.         int result = 1;  
  39.         result = prime * result + age;  
  40.         result = prime * result + ((name == null) ? 0 : name.hashCode());  
  41.         return result;  
  42.     }  
  43.   
  44.     @Override  
  45.     public boolean equals(Object obj) {  
  46.         if (this == obj)  
  47.             return true;  
  48.         if (obj == null)  
  49.             return false;  
  50.         if (getClass() != obj.getClass())  
  51.             return false;  
  52.         Person other = (Person) obj;  
  53.         if (age != other.age)  
  54.             return false;  
  55.         if (name == null) {  
  56.             if (other.name != null)  
  57.                 return false;  
  58.         } else if (!name.equals(other.name))  
  59.             return false;  
  60.         return true;  
  61.     }  
  62.   
  63. }  

在测试类中,开始这样子做:

[java]  view plain copy print ?
  1. public class TestPerson {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Set<Person> set = new HashSet<Person>();  
  5.         Person p1 = new Person("喜洋洋"25);  
  6.         Person p2 = new Person("懒洋洋"26);  
  7.         Person p3 = new Person("灰太郎"27);  
  8.         set.add(p1);  
  9.         set.add(p2);  
  10.         set.add(p3);  
  11.         System.out.println(set.size() + " 个动画人物!");  
  12.         // 删除一个对象  
  13.         set.remove(p2);  
  14.         System.out.println("删除之后," + set.size() + " 个动画人物!");  
  15.         for (Person person : set) {  
  16.             System.out.println(person);  
  17.         }  
  18.     }  
  19. }  

打印结果:

[java]  view plain copy print ?
  1. 3 个动画人物!  
  2. 删除之后,2 个动画人物!  
  3. age=27, name=灰太郎  
  4. age=25, name=喜洋洋  

成功删除一个对象,再次修改测试类的代码:

[java]  view plain copy print ?
  1. public class TestPerson {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Set<Person> set = new HashSet<Person>();  
  5.         Person p1 = new Person("喜洋洋"25);  
  6.         Person p2 = new Person("懒洋洋"26);  
  7.         Person p3 = new Person("灰太郎"27);  
  8.         set.add(p1);  
  9.         set.add(p2);  
  10.         set.add(p3);  
  11.         System.out.println(set.size() + " 个动画人物!");  
  12.         // 修改对象属性  
  13.         p2.setName("美人鱼");  
  14.         // 删除一个对象  
  15.         set.remove(p2);  
  16.         System.out.println("删除之后," + set.size() + " 个动画人物!");  
  17.         for (Person person : set) {  
  18.             System.out.println(person);  
  19.         }  
  20.     }  
  21. }  

打印结果:

[java]  view plain copy print ?
  1. 3 个动画人物!  
  2. 删除之后,3 个动画人物!  
  3. age=26, name=美人鱼  
  4. age=27, name=灰太郎  
  5. age=25, name=喜洋洋  

这次怪了,明明删除一个了,怎么还是有三个呢?你会发现,的确删除一个“懒洋洋”,但是“美人鱼”没有被删除!

如果你在 Person 类中,不重写 hashCode 方法,不会有这种现象发生!


这里说明一个问题:添加到集合的类,不要轻易去修改该类对象的属性,否则 remove() 方法无效。同理 contains() 方法也会无效。


如果有兴趣的话,可以看看其源码,可以看出这与 hashCode() 方法有很大关系!


再说一个容易让人误解的问题:

Collection接口的子接口 List 和 Set,Set (包括其子类)无序不可重复,List (包括其子类)有序可重复,所谓有序无序是相对于 add 的程序执行顺序来说的。


换句话说,对于上面的 List、Set 以及其子类等,如果 equals 为 true 的话,就算是重复的对象。这里的 equals 比较的是内容,不是对象地址。
只不过,对于 Set 来说不可以添加重复对象,对于 List 来说可以添加重复对象!


对于添加对象到Set集合中,从源码可以看出其流程是这样子的:


将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。
如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。


举个例子,注意:Person 要重写 hashCode、equals 方法:

[java]  view plain copy print ?
  1. public static void main(String[] args) {  
  2.         LinkedList<Person> list = new LinkedList<Person>();  
  3.         Set<Person> set = new HashSet<Person>();  
  4.         Person p1 = new Person("喜喜"3);  
  5.         Person p2 = new Person("喜喜"3);  
  6.         System.out.println("stu1 == stu2 : " + (p1 == p2));  
  7.         System.out.println("stu1.equals(stu2) : " + p1.equals(p2));  
  8.         // list可以重复  
  9.         list.add(p1);  
  10.         list.add(p2);  
  11.         System.out.println("list size:" + list.size());  
  12.         // set 不可以重复  
  13.         set.add(p1);  
  14.         set.add(p2);  
  15.         System.out.println("set size:" + set.size());  
  16.     }  


打印结果:

[java]  view plain copy print ?
  1. stu1 == stu2 : false  
  2. stu1.equals(stu2) : true  
  3. list size:2  
  4. set size:1  

感谢下面两篇博客,我只是在它们的基础之上添枝加叶。


再次感谢:


hashCode与equals的区别与联系


Java集合HashSet的hashcode方法引起的内存泄漏问题


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用HashSet时,如果要将自定义对象作为元素存储在其,则需要重写该对象的hashCode()和equals()方法hashCode()方法用于确定元素的哈希值,用于在HashSet确定元素的位置。 equals()方法用于比较两个元素是否相等。如果两个元素的hashCode()值相同,则会调用equals()方法来确定它们是否相等。 重写两个方法的规则如下: - hashCode()方法: 1. 在同一对象多次调用hashCode()应该返回相同的整数。 2. 如果equals()比较两个对象相等,则它们的hashCode()返回值应该相同。 3. hashCode()返回值不一定唯一,不同对象可能会返回相同的整数。 - equals()方法: 1. 自反性: 对于任何非空引用x,x.equals(x)应该返回true。 2. 对称性: 对于任何非空引用x和y,当且仅当y.equals(x)返回true时,x.equals(y)也应该返回true。 3. 传递性: 对于任何非空引用x,y,z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也应该返回true。 4. 一致性:对于任何非空引用x和y,多次调用x.equals(y)应该始终返回相同的结果在使用HashSet的时候,为了保证对象的唯一性,需要重写对象的hashCodeequals方法hashCode方法用于生成对象的哈希码,HashSet在添加对象时会使用该哈希码来判断对象是否重复。 equals方法用于判断两个对象是否相等。如果重写hashCode方法,一般也要重写equals方法重写两个方法时,需要遵循以下规则: 1.如果两个对象相等,那么它们的hashCode值一定相等。 2.如果两个对象的hashCode值相等,那么它们不一定相等。 3.equals方法需要遵循传递性,对称性和自反性

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值