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类型除去)),可以看该引用对应的对象的属性值是否相同

浙公网安备 33010602011771号