欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

重写equal和hashCode方法,用集合去重对象

程序员文章站 2022-04-15 14:38:12
...

综述:
  • equal和hasCode方法讲解
  • 重写equal和hasCode,用HashSet集合进行对象去重
  • 自我理解
equal和hasCode
  1. equal和hasCode都是Object中的方法,所有的类都有这这两种方法。
    先看代码,在讲解

user实体类

/**
 * 根据用户的firstName和lastName确定是否是这个用户
 */
public class User {
        private String firstName;
        private String lastName;

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
}
public class EqualDemo {
    public static void main(String[] args) {
        /**
         * step1 基本类型比较:所有基本类型直接比较的是值
         */
        Integer i1 = 1;
        Integer i2 = 1;
        System.out.print("基本类型Integer比较......: == —>");
        System.out.print(i1.equals(i2));
        System.out.print(" equal —>");
        System.out.println(i1.equals(i2));
        /**
         * step2 引用类型比较:一种比较String类型,一种比较User对象类型
         */
        String s1 = "abc";
        String s2 = new String("abc");
        String s3 = "abc";
        System.out.print("引用类型String比较......: == —>");
        System.out.print(s1 == s2);
        System.out.print(" s1和s3 —>");
        System.out.print(s1 == s3);
        System.out.print(" equal —>");
        System.out.println(s1.equals(s2));

        User u1 = new User();
        u1.setFirstName("wang");
        u1.setLastName("dan");
        User u2 = new User();
        u2.setFirstName("wang");
        u2.setLastName("dan");
        System.out.print("引用类型User对象比较......: == —>");
        System.out.print(u1 == u2);
        System.out.print(" equal —>");
        System.out.println(u1.equals(u2));

    }

结果

基本类型Integer比较......: ==>true equal —>true
引用类型String比较......: ==>false s1和s3 —>true equal —>true
引用类型User对象比较......: ==>false equal —>false
  1. 详谈
    1)基本类型包括八种,每一种基本类型都重写了Object中的equal方法,基本类型在JVM中引用直接指向的就是当前基本类型的值(见下图),所以基本类型中equal方法和==都是值比较。
    重写equal和hashCode方法,用集合去重对象
    2)引用类型在JVM中,栈中的引用指向的是堆内存放值的地址,然后可以根据地址取出值进行比较。String类型’==’不相等是因为引用s1和s2指向的地址不相同,所以他们不相等,但是s1和s3相等,因为s1和s3指向的地址相同,变量s1和s3初始化时,首先会检查方法区中常量中是否有’abc’这个值,如果存在会将s3的引用直接指向这个值,如果不存在就会在常量池中创建这个值,然后将变量引用指向这个值。String类型中’equal’方法比较特殊,因为在String中重写了这个方法,他比较的是引用指向的地址的值的大小。
    3)User对象的比较的无论是equal还是==都是不相等的,因为他并没有直接比较的是对象指向的地址是否相同,u1和u2两个对象都是new出来的,所以他们的地址值肯定不相同。如果想用equal比较其他的值,需要在User对象内存重写equal方法。
  2. hasCode最常见的地方就是HashSet和HashMap,根据哈希求值将元素存储到集合中。
重写equal和hasCode

这个地方能墨迹一点,因为我想分别说明一下,觉得还是有必要的,耐心的看下去,你就会发现为什么要同时重写这两个方法。
实体代码OvriUser ,重写equalsh和hashCode方法,如下1,2点都是注释掉了没有重写的部分,会在重写hashCode方法的地方说一下hashCode方法。

public class OvriUser {
    private String firstName;
    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public boolean equals(Object o) {
        OvriUser u = (OvriUser) o;
        return this.firstName == u.getFirstName() && this.lastName == u.getLastName();
    }
    @Override
    public int hashCode() {
        int res = 7;
        res = res + firstName.hashCode()+lastName.hashCode();
        return res;
    }
}
  1. 对象重equal方法,没有重写hashCode方法
    结果:在地方部分展示所有比较的代码
重写equals方法......: ==>false equal —>true
没有重写hashCode Ovriuser: 
356573597 1735600054

重写equals方法,在对象比较的时候,值会相同,因为在重写的equals方面里面会比较这两个值,没有重写的hashCode值是不相同的。
2. 对象重写hashCode方法,不重写equal方法
结果:在地方部分展示所有比较的代码

没有重写equals方法......: ==>false equal —>false
重写hashCode Ovriuser: 
3741083 3741083

重写hashCode方法的结果,hashCode值是相同的,equals确实不相同的。hashCode方法就是自己制定一个规则,几个计算方法。
3. 对象重写equal和hashCode方法

全部重写的代码

public class OvriTest {
    public static void main(String[] args) {
        OvriUser o1 = new OvriUser();
        o1.setFirstName("wang");
        o1.setLastName("dan");
        OvriUser o2 = new OvriUser();
        o2.setFirstName("wang");
        o2.setLastName("dan");
        /**
         * 重写equals方法,没有重写hashCode方法
         */
        System.out.print("......: == —>");
        System.out.print(o1 == o2);
        System.out.print(" equal —>");
        System.out.println(o1.equals(o2));

        System.out.println("hashCode Ovriuser: ");
        System.out.print(o1.hashCode());
        System.out.print(" "+o2.hashCode());

        /**
         * 没有重写equals方法,重写hashCode方法
         */
        System.out.print("equals方法......: == —>");
        System.out.print(o1 == o2);
        System.out.print(" equal —>");
        System.out.println(o1.equals(o2));

        System.out.println("重写hashCode Ovriuser: ");
        System.out.print(o1.hashCode());
        System.out.print(" "+o2.hashCode());
    }
}

全部重写之后发现,equals和hashCode的值是相同的。
结果:

......: ==>false equal —>true
hashCode Ovriuser: 
3741083 3741083
equals方法......: ==>false equal —>true
重写hashCode Ovriuser: 
3741083 3741083
  1. 用HashSet进行对象去重
    HashSet可以对重写了hashCode和equals方法的实体进行去重复。我们首先看下HashSet的数据结构:
    重写equal和hashCode方法,用集合去重对象
    (1)对于重写equals方法的实体(Ovriuser),他们的哈希值不相同,即num值不相同,在HashSet去重的时候计算相同实体的哈希值不同,所以不能够去重复。(上一步中的第一点)
    (2)对于重写hasCode方法的实体,虽然能够得到相同的num值,但是他们在判断相等的时候结果是不相等。(上一步中的第二点)
    (3)只有重写了equals和hasCode方法的才能用集合HashSet对实体(Ovriuser)进行去重。
重写equal和hasCode

其实HashSet本质上就是HashMap的键,如下为HashMap的数据结构:

重写equal和hashCode方法,用集合去重对象
(1)HashMap底层的实现就是一个线性数组+链表,HashMap和HashSet中hasCode方法作用都是一样的,就是求出哈希值,然后找到在哈希值在线性数组中的位置。equals方法对于HashSet来说就是重复用的,如果对象A、B的哈希值相同,equals值相同那么对象A、B就是重复对象,去掉一个即可。如果是HashMap中存在A、B、C三个对象的哈希值相同,equals值相同,那么他们存在哈希取值之后会出现冲突,链表的作用就是解决冲突的,当然链表的长度也不能够无线长,详情请了解哈希相关的,上图就是A、B、C在HashMap中的存储结构。
(2)HashMap都是通过put(key,value)对值进行存储的,相同哈希值的对象会以Map.Entry的形式存放到链表对应的位置。使用HashMap的get()方法时是如何找到对应的值得呢?同一个哈希对应着么多的对象,那么多的值,因为在链表的每个节点处存放的是一个节点Node