“==” 比较示例如下:
Compare compare1 = new Compare("name");
Compare compare2 = new Compare("name");
//compare1 指向内存中的对象 和 compare1指向内存中的对象不是同一个,因此结果为 false
System.out.println(compare1 == compare2);
equals 比较示例如下:
Compare compare1 = new Compare("name");
Compare compare2 = new Compare("name");
//若对象的equals方法没有重写,默认也是==,结果为false;若对象的equals方法重写了,结果为true
System.out.println(compare1.equals(compare2));
equals方法没有重写时,调用的是Object的equals方法,源码如下:
public boolean equals(Object obj) {
//Ojbect 的 equals 方法,实际就是 ==
return (this == obj);
}
重写后的equals方法如下:
@Override
public boolean equals(Object o) {
//先判定是否是同一个对象,若是,那么内容必定相等。
if (this == o) return true;
//在判定是否是指定类型或其子类的实例,若不是,返回false。(相当于不能比较)
if (!(o instanceof Compare)) return false;
Compare compare = (Compare) o;
//若是,调用 Objects.equals 方法进行比较。
return Objects.equals(name, compare.name);
}
上面提到的 Objects.equals 方法,推荐使用,它避免了我们平时进行equals比较时,调用方的空指针问题。源码如下:
public static boolean equals(Object a, Object b) {
//a.equals(b) 是 Object 的 equals 方法
//a != null 是 避免空指针问题的关键所在。
return (a == b) || (a != null && a.equals(b));
}
如果重写了equals()没有重写hashCode(),这两个实例类有可能是逻辑上相等,但是jvm只看Ojbect.hashCode()方法,所以jvm还是认为这是两个对象,因此违反了相等的对象必须具有相等的散列码,所以重写equals方法的时候一般都需要同时重写hashCode方法。
场景如下:(重写了equals方法,但未重写hashcode方法)
Compare compare1 = new Compare("name");
Compare compare2 = new Compare("name");
System.out.println(compare1.equals(compare2));//true
HashSet<Compare> set = new HashSet<>();
set.add(compare1);
set.add(compare2);
System.out.println(set.size());//2
我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 Compare 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。
HashSet是如何判断新增元素是否重复的?
首先我们要知道HashSet底层的数据结构是哈希表。根据哈希表得出的哈希值代表该对象的储存位置。
HashSet先调用元素对象的hashcode方法,JVM虚拟机来判个对象在物理位置上是否相等,也就是看两个元素的哈希值是否相等,如果不等那肯定是不同对象,就直接添加进set集合。
如果相等,再根据两个元素对象的equals方法判断在业务上是否相等,是否返回true,为ture则被认为是相同对象,不能重复添加,为false则可以添加。