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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jtracydy/article/details/78819806

综述:
  • 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方法和==都是值比较。
    这里写图片描述
    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的数据结构:
    这里写图片描述
    (1)对于重写equals方法的实体(Ovriuser),他们的哈希值不相同,即num值不相同,在HashSet去重的时候计算相同实体的哈希值不同,所以不能够去重复。(上一步中的第一点)
    (2)对于重写hasCode方法的实体,虽然能够得到相同的num值,但是他们在判断相等的时候结果是不相等。(上一步中的第二点)
    (3)只有重写了equals和hasCode方法的才能用集合HashSet对实体(Ovriuser)进行去重。
重写equal和hasCode

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

这里写图片描述
(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

猜你喜欢

转载自blog.csdn.net/jtracydy/article/details/78819806