HashSet如何保证元素唯一性的原理

1.HashSet原理

a. 我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率, 降低了使用equals()方法的次数

b. 当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象

c. 如果没有哈希值相同的对象就直接存入集合

d.如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存

2.将自定义类的对象存入HashSet去重复

a. 类中必须重写hashCode()和equals()方法

b. hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)

c. equals(): 属性相同返回true, 属性不同返回false,返回false的时候存储

必须重写hashCode()和equals()方法(当hash值相同时就会自动调用equals方法来判断两个对象是否相同)

注意: 当没有在Person类中重写hashCode()方法和equals()方法时, 则运行结果会出现相同的信息, 因为HashSet虽然会保证元素不可重复, 但是需要依据hashCode()方法和equals()方法 , 因为Person类中未重写这两个方法, 但是会使用Person类中从Object类里继承的hashCode()和equals(), 然而Object中的hashCode()比较的是对象的地址, 而每new出一个对象地址总是不同的, 所以会出现存储相同的元素.

Person实体类:

  1. package online.msym.bean;
  2. public class Person {
  3. private String name;
  4. private int age;
  5. public Person() {
  6. super();
  7. }
  8. public Person(String name, int age) {
  9. super();
  10. this.name = name;
  11. this.age = age;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public int getAge() {
  20. return age;
  21. }
  22. public void setAge(int age) {
  23. this.age = age;
  24. }
  25. @Override
  26. public String toString() {
  27. return "Person [name=" + name + ", age=" + age + "]";
  28. }
  29. @Override
  30. public boolean equals(Object obj) {
  31. System.out.println("equals:执行了");
  32. Person p = (Person)obj;
  33. return this.name.equals(p.name) && this.age == p.age;
  34. }
  35. @Override
  36. public int hashCode() {
  37. System.out.println("hashCode :执行了");
  1. //这里一直返回10是为了证明hashSet集合判断元素对象是否相同,会先执行hashCode方法,返回值相同时,会再去调用对象的equals方法
  1. return 10;
  2. }
  3. }

测试类:

  1. package online.msym.mytest;
  2. import java.util.HashSet;
  3. import online.msym.bean.Person;
  4. public class Demo1_HashSet {
  5. /**
  6. * 注意:我们认为姓名和年龄都相同时表示时同一个人
  7. * 如果:Person类中没有重写equals方法的话,无法去重,但是重写后还是无法去除重复的内容
  8. * 因为HashSet判断元素是否重复,首先根据元素的hashCode值,而我们并没有重写Person类的hashCode方法,
  9. * 此时调用的是Object中的hashCode方法,而此方法是根据对象的地址值计算hashCode值,而只要是new出来的对象,
  10. * 地址值肯定不同,所以计算出来的hashCode值也不相同,就认为不是同一个对象,也不会执行equals方法
  11. * 如果我们重写了hashCode方法,先简单的返回一个数字10,这样当判断hashCode相等时就会去判断equals方法的返回值,
  12. * 就能够去除重复
  13. * @param args
  14. */
  15. public static void main(String[] args) {
  16. HashSet<Person> hs = new HashSet<>();
  17. hs.add(new Person("张三", 23));
  18. hs.add(new Person("张三", 23));
  19. hs.add(new Person("李四", 24));
  20. hs.add(new Person("李四", 24));
  21. hs.add(new Person("李四", 24));
  22. hs.add(new Person("李四", 24));
  23.  
  24. //System.out.println(hs.size());
  25. System.out.println(hs);
  26. }
  27. }

修改后的Person实体类:实现Compareable接口,就具备了比较的功能了。

  1. package online.msym.bean;
  2. public class Person implements Comparable<Person> {
  3. private String name;
  4. private int age;
  5. public Person() {
  6. super();
  7.  
  8. }
  9. public Person(String name, int age) {
  10. super();
  11. this.name = name;
  12. this.age = age;
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. this.name = name;
  19. }
  20. public int getAge() {
  21. return age;
  22. }
  23. public void setAge(int age) {
  24. this.age = age;
  25. }
  26. @Override
  27. public String toString() {
  28. return "Person [name=" + name + ", age=" + age + "]";
  29. }
  30. /*@Override
  31. public boolean equals(Object obj) {
  32. System.out.println("执行了吗");
  33. Person p = (Person)obj;
  34. return this.name.equals(p.name) && this.age == p.age;
  35. }
  36. @Override
  37. public int hashCode() {
  38. //40+23 39+24
  39. //return name.hashCode()+age;
  40. final int NUM = 38;
  41. return name.hashCode() * NUM + age;
  42. }*/
  43. //Eclipse帮我们自动生成的hashCode和equals方法如下,alt+shift+s->h
  44. /*
  45. * 为什么是31?
  46. * 1,31是一个质数,质数是能被1和自己本身整除的数
  47. * 2,31这个数既不大也不小
  48. * 3,31这个数好算,2的五次方-1,2向左移动5位
  49. */
  50. @Override
  51. public int hashCode() {
  52. final int prime = 31;
  53. int result = 1;
  54. result = prime * result + age;
  55. result = prime * result + ((name == null) ? 0 : name.hashCode());
  56. return result;
  57. }
  58. @Override
  59. public boolean equals(Object obj) {
  60. if (this == obj) //调用的对象和传入的对象是同一个对象
  61. return true; //直接返回true
  62. if (obj == null) //传入的对象为null
  63. return false; //返回false
  64. if (getClass() != obj.getClass()) //判断两个对象对应的字节码文件是否是同一个字节码
  65. return false; //如果不是直接返回false
  66. Person other = (Person) obj; //向下转型
  67. if (age != other.age) //调用对象的年龄不等于传入对象的年龄
  68. return false; //返回false
  69. if (name == null) { //调用对象的姓名为null
  70. if (other.name != null) //传入对象的姓名不为null
  71. return false; //返回false
  72. } else if (!name.equals(other.name)) //调用对象的姓名不等于传入对象的姓名
  73. return false; //返回false
  74. return true; //返回true
  75. }
  76. }

【点击此处回到主页】

集合框架(HashSet存储自定义对象保证元素唯一性)的更多相关文章

  1. 集合框架-HashSet存储自定义对象

    1 package cn.itcast.p4.hashset.test; 2 3 import java.util.HashSet; 4 import java.util.Iterator; 5 6 ...

  2. Java基础知识强化之集合框架笔记40:Set集合之HashSet存储自定义对象并遍历

    1. HashSet存储自定义对象并遍历 2. 代码示例: (1)Student类,如下: package cn.itcast_02; /** * @author Administrator * */ ...

  3. Java基础知识强化之集合框架笔记41:Set集合之HashSet存储自定义对象并遍历练习

    1. HashSet集合存储自定义对象并遍历.如果对象的成员变量值相同即为同一个对象 注意了: 你使用的是HashSet集合,这个集合的底层是哈希表结构. 而哈希表结构底层依赖:hashCode()和 ...

  4. 30.1 HashSet存储自定义对象 未去重解决

    问题: package day30_HashSet; import java.util.HashSet; /* * 通过hashset存储自定义对象,没有进行去重. * * */ public cla ...

  5. Java基础知识强化之集合框架笔记45:Set集合之TreeSet存储自定义对象并遍历练习1(自然排序:Comparable)

    1. 自然排序: TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按照升序排列,这种方式就是自然排序. Java中提供了一个Comp ...

  6. Java基础知识强化之集合框架笔记46:Set集合之TreeSet存储自定义对象并遍历练习2(自然排序:Comparable)

    1. TreeSet存储自定义对象并遍历练习2: (1)Student.java package cn.itcast_06; /* * 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口 * ...

  7. 用HashSet存储自定义对象

      案例 package cn.itcast_02; import java.util.HashSet; /* * 需求:存储自定义对象,并保证元素的唯一性 * 要求:如果两个对象的成员变量值都相同, ...

  8. 《java入门第一季》之HashSet存储自定义对象问题以及注意事项

    上一篇http://blog.csdn.net/qq_32059827/article/details/51578158 写到存储字符串类型的时候出现了无序,而且这个无序不是随机那种无序,它是有一定存 ...

  9. 《java入门第一季》之TreeSet存储自定义对象并保证排序和唯一

    上一篇用一个简单的例子,介绍了treeset集合存储的内部过程,这里再完善其存储自定义对象保证唯一. 需求:A:  * 自然排序,按照年龄从小到大排序  *         B:  * 成员变量值都相 ...

随机推荐

  1. 无线网络中,使用MDK3把指定的用户或者热点踢到掉线

    准备 1:系统环境为ubuntu16.04, 2:需要mdk3, mdk3这个软件需要通过apt安装, 需要kali系统的源 3:需要安装aircrack-ng套件 今天这套东西,可以在未连接上靶机网 ...

  2. SQL2012还原数据库操作在本地服务器上操作和用别的电脑远程连接到服务器进行操作的文件路径差异

    在数据库服务器上想还原一个数据库到某个备份文件时期的,服务器的数据库文件本身是保存在 D:\DEVDB目录 通过开发电脑上的MS manager来连接数据库服务器操作还原 虽发现文件卡项上,原始文件名 ...

  3. PrintWriter用法简析

    public class PrintWriterextends Writer 向文本输出流打印对象的格式化表示形式.此类实现在 PrintStream 中的所有 print 方法.它不包含用于写入原始 ...

  4. JAVA 命令行参数解析,org.apache.commons.cli的使用

    maven依赖引入 <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cl ...

  5. 怎么监控apache运行状态和页面统计

    通过使用mod_status的模块来监控Apache web server的负载. 1. mod_status是什么? mod_status是一个apache模块,它帮助监控web server负载和 ...

  6. 怎么在ubuntu上运行php代码?

    1. 首先,你需要安装Apache2. sudo apt-get update sudo apt-get install apache2 当安装完以后,Apache就已经开始运行啦,你可以进行测试,通 ...

  7. [Netty] - Netty IN ACTION(导言)

    最近没什么事儿做,刚好看到有需要网络编程的知识,java中有NIO和IO两种不同的方式,但是NIO的编写比较麻烦,刚好找到一个成熟的网络框架Netty.接下来的一个月就准备将Netty IN ACTI ...

  8. Java_HelloWorld

    Java_HelloWorld 一.JDK安装与环境变量的设置 可以在甲骨文公司的主页上直接下载. 链接:http://www.oracle.com/technetwork/java/javase/d ...

  9. hbase中Compaction的理解及RegionServer内存的使用,CacheBlock机制

    Compaction有两种类型: (1)minor compaction:属于轻量级.将多个小的storefile文件重写为数量较少的大storefile文件,减少存储文件的数量,实际上是个多路归并的 ...

  10. storyboard页面跳转传值

    受学姐的影响,习惯纯代码编程,这次要修改别人的代码,很多编程风格还不习惯. 在此之前,页面跳转我都用的是Navigation,故事板上的页面跳转带传值,让我卡了好半天. 页面跳转: [self per ...