equals与==的区别
1、一个对象的hashCode取是对象在JVM保存的值,不同的引用对象分配堆的位置不一样,hashCode一定不同。
2、== 比较引用对象,比较的是两个对象hashcode 值, == 比较两个常量总是指向常量池中同一个对象,所以总是相同的。
3、 在不重写hashcode 和equals情况下,Object对象equal底层也是== 实现,比较两个对象的内存地址,== 和equals没有什么区别。
4、 在重写equal 和hashcode时一般遵循一个原则:
重写equals也重写hashcode,并且保证equals相等的对象,hashCode也相等。
从业务角度考虑:如果你只重写了equals,没有重写hashCode,可能导致equals相同,hashCode不相等。在应用层面使用equals写业务逻辑判断对象是否相同,插入到集合中,集合中使用hashCode作为判断是否相同的对象,同一个集合中存储了对各对象,这和你编写代码的初衷不一致。
为什么HashMap两个不同的对象的可能映射到同一个位置?
因为并没有并没有直接使用key对象的内存地址作为映射地址,而是对key的内存地址进行一个hash函数映射。HashSet存储时先判断key得到hashcode()是否相同,hashcode值相同(此时只能说映射到了同一个位置,产生了哈希冲突,不能说key值本身内存地址有重复),还需要使用equals()函数对key进行判断,equals也相同,则认为元素已经存在,默认equals中使用对象地址判断两个对象是存在,内存地址不相同的两个对象一定不相等。
为什么不直接使用对象内存地址哈希映射值?
HashMap和HashSet的key值直接,内存地址很大,不映射直接作为hash值,存储元素非常少时,会造成极大的空间浪费。
应用举例:创建一个学生类,在各种集合中,只要名字相同就认为是同一个学生。
hashCode的重写实现需要满足不变性,即一个object的hashCode不能前一会是1,过一会就变成2了。hashCode的重写实现最好依赖于对象中的final属性,从而在对象初始化构造后就不再变化。
public class Student {
private String name;
private Integer age;
public Student(){}
public Student(String name) {
this.name = name;
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
return ((Student)obj).getName().equals(this.name);
}
@Override
public int hashCode() {
return this.name.hashCode();
}
}
测试类
public class Main1 {
public static void main(String[] args) {
Student stu1= new Student("张三",12);
Student stu2 = new Student("张三",14);
System.out.println(stu1 == stu2);//重写hashCode和equals前后,都是false
System.out.println(stu1.equals(stu2));//重写equals前时false,重写后时true
HashMap<Student,Integer> map = new HashMap<>();
map.put(stu1,1);
map.put(stu2,2);
System.out.println(map.size());//重写hashCode和equals之前size=2,同时重写equal和false后为size =1,只重写equals,size=2
}
}