java中equals和hashcode的区别

1.java中hash集合添加元素

          JDK对equals(Object obj)和hashcode()这两个方法的定义和规范: 在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。 equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。 hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。 接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个):

         规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。

         规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。 根据这两个规范,可以得到如下推论:

                     1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。

                     2、如果两个对象不equals,他们的hashcode有可能相等。

                     3、如果两个对象hashcode相等,他们不一定equals。

                     4、如果两个对象hashcode不相等,他们一定不equals。 
这样我们就可以推断Java运行时环境是怎样判断HashSet和HastSet中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。 
测试程序如下:首先我们定义一个类,重写hashCode()和equals(Object obj)方法

public class ATSD {
    @Test
    public void Test(){
        Set<A>set=new HashSet<>();
        A a1=new A();
        System.out.println("开始set a1......");
        set.add(a1);
        System.out.println("添加a1成功。。。。。。");
        A a2=new A();
        System.out.println("开始set a2......");
        set.add(a2);
        System.out.println("添加a2成功。。。。。。");
        System.out.println(set);
    }
}
class A {
    @Override
    public boolean equals(Object obj) {
        System.out.println("判断equals");
        return false;
    }
    @Override
    public int hashCode() {
        System.out.println("判断hashcode");
        return 1;
    }
}

运行结果:

开始set a1......
判断hashcode
添加a1成功。。。。。。
开始set a2......
判断hashcode
判断equals
添加a2成功。。。。。。
判断hashcode
判断hashcode
[com.bjmashibing.system.baierhu.stream.DateFormat.io.A@1, com.bjmashibing.system.baierhu.stream.DateFormat.io.A@1]

这表示了hashset添加元素时,会先判断hashcode,假设hashcode不同,则直接插入,如果hashcode相同,开始调用equals()方法来判断元素是否相同

因为hashset是不重复的,所以这样就有了一个问题,假设我重写了equals方法,hashcode不变,如果equals之后返回true,即我们认为两个相同的对象,插入到hashset时,先用hashcode方法判断时就发现不相等,直接全部插入,和我们的需求就会有所冲突,如下

2.为什么重写equals必须重写hashcode

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class Student {
    private Integer age;
    private String name;
    private User user;

    @Override
    public boolean equals(Object o) {
        System.out.println("调用equals");
        return true;
    }

    @Override
    public int hashCode() {
        System.out.println("调用hashcode");
        return Objects.hash(age);
    }
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class User{
    private String userName;
}
    @Before
    public void init(){
        students=new HashSet<Student>();
        students.add(null);
        students.add(new Student(0,"第零",new User("baiuser")));
        students.add(new Student(0,"第一",new User("baiuser")));
        students.add(null);
        students.add(new Student(2,"第二",new User("zhaowen")));
        students.add(new Student(4,"第三",new User("wangmancang")));
        students.add(new Student(3,"第四",new User("wangmancang")));
    }

测试运行:

 @Test
    public void hashSet(){
        System.out.println("students = " + students);
        Student student=new Student();
        Student student1=new Student();
        System.out.println("student.equals(student1)============");
        System.out.println(student.equals(student1));
        System.out.println("student1==student================");
        System.out.println(student1==student);
        List<Student>list=Arrays.asList(new Student(1,"ll",new User()),new Student(1,"",new User()));
        System.out.println("list distinct方法去重");
        list=list.stream().distinct().collect(Collectors.toList());
        System.out.println(list);
    }

运行结果:

调用hashcode
调用hashcode
调用equals
调用hashcode
调用hashcode
调用hashcode
students = [null, Student(age=2, name=第二, user=User(userName=zhaowen)), Student(age=3, name=第四, user=User(userName=wangmancang)), Student(age=4, name=第三, user=User(userName=wangmancang)), Student(age=0, name=第零, user=User(userName=baiuser))]
student.equals(student1)============
调用equals
true
student1==student================
false
list distinct方法去重
调用hashcode
调用hashcode
调用hashcode
调用equals
[Student(age=1, name=ll, user=User(userName=null))]

根据运行结果来看:

1.hashSet和hashMap符合上述的观点,当hashcode不同时,认为不是同一个对象,当hashcode相同时,还要看equas方法判断,才能决定是否加入的hash集合中去

2.当调用equas方法时,和hashcode没有半毛钱关系

3.== 是判断地址是否是同一个,所以和equals以及hashcode都没有半毛钱关系

4.有意思的是jdk1.8之后的stream流中的distinct方法在使用的时候会先判断hashcode再判断equals

通常我们在使用去重时,hashcode值其实是对象属性的hashcode拼接而成:

public class EqualsAndHashCode {
    @Test
    public void distinctTest(){
        Student student=   new Student(1,"2",new User("1"));
        Student student1=   new Student(1,"2",new User("1"));
        List<Student>stu=Arrays.asList(student,student1);
        System.out.println(stu.stream().distinct().collect(Collectors.toList()));
    }


}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class Student {
    private Integer age;
    private String name;
    private User user;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(age, student.age) &&
                Objects.equals(name, student.name) &&
                Objects.equals(user, student.user);
       }

       @Override
        public int hashCode() {
             return Objects.hash(age, name, user);
        }
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
class User{
    private String userName;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(userName, user.userName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(userName);
    }
}

输出结果:

[Student(age=1, name=2, user=User(userName=1))]

所以默认当对象中的所有属性值相同时该对象认为是同一个对象(当对象中的属性是引用属性时(String类型除去)),可以看该引用对应的对象的属性值是否相同

posted @ 2021-05-09 17:08  小虎。。。。  阅读(72)  评论(0)    收藏  举报