zoukankan      html  css  js  c++  java
  • 集合框架(HashSet存储自定义对象保证元素唯一性)

    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实体类:

    package online.msym.bean;
    public class Person {
        private String name;
        private int age;
        public Person() {
            super();        
        }
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
        @Override
        public boolean equals(Object obj) {
            System.out.println("equals:执行了");
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age;
        }
        @Override
        public int hashCode() {
            System.out.println("hashCode :执行了");
            //这里一直返回10是为了证明hashSet集合判断元素对象是否相同,会先执行hashCode方法,返回值相同时,会再去调用对象的equals方法
    return 10;        
        }    
    }

    测试类:

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

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

    package online.msym.bean;
    public class Person implements Comparable<Person> {
        private String name;
        private int age;
        public Person() {
            super();
            
        }
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
        /*@Override
        public boolean equals(Object obj) {
            System.out.println("执行了吗");
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age;
        }
        @Override
        public int hashCode() {
            //40+23  39+24
            //return name.hashCode()+age;
            final int NUM = 38;
            return name.hashCode() * NUM + age;
        }*/
        //Eclipse帮我们自动生成的hashCode和equals方法如下,alt+shift+s->h
        /*
         * 为什么是31?
         * 1,31是一个质数,质数是能被1和自己本身整除的数
         * 2,31这个数既不大也不小
         * 3,31这个数好算,2的五次方-1,2向左移动5位
         */
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)                        //调用的对象和传入的对象是同一个对象
                return true;                        //直接返回true
            if (obj == null)                        //传入的对象为null
                return false;                        //返回false
            if (getClass() != obj.getClass())        //判断两个对象对应的字节码文件是否是同一个字节码
                return false;                        //如果不是直接返回false
            Person other = (Person) obj;            //向下转型
            if (age != other.age)                    //调用对象的年龄不等于传入对象的年龄
                return false;                        //返回false
            if (name == null) {                        //调用对象的姓名为null
                if (other.name != null)                //传入对象的姓名不为null
                    return false;                    //返回false
            } else if (!name.equals(other.name))    //调用对象的姓名不等于传入对象的姓名
                return false;                        //返回false
            return true;                            //返回true
        }    
    }

    【点击此处回到主页】

  • 相关阅读:
    各国语言缩写列表,各国语言缩写-各国语言简称,世界各国域名缩写
    How to see log files in MySQL?
    git 设置和取消代理
    使用本地下载和管理的免费 Windows 10 虚拟机测试 IE11 和旧版 Microsoft Edge
    在Microsoft SQL SERVER Management Studio下如何完整输出NVARCHAR(MAX)字段或变量的内容
    windows 10 x64系统下在vmware workstation pro 15安装macOS 10.15 Catelina, 并设置分辨率为3840x2160
    在Windows 10系统下将Git项目签出到磁盘分区根目录的方法
    群晖NAS(Synology NAS)环境下安装GitLab, 并在Windows 10环境下使用Git
    使用V-2ray和V-2rayN搭建本地代理服务器供局域网用户连接
    windows 10 专业版安装VMware虚拟机碰到的坑
  • 原文地址:https://www.cnblogs.com/daimajun/p/6524717.html
Copyright © 2011-2022 走看看