HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键。
那么Java运行时环境是如何判断HashSet中相同对象、HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢?
在研究这个问题之前,首先说明一下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和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。
测试程序如下:首先我们定义一个类,重写hashCode()和equals(Object obj)方法
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判断equals");
return false;
}
@Override
public int hashCode() {
System.out.println("判断hashcode");
return 1;
}
}
然后写一个测试类,代码如下:
运行之后打印结果是:
判断hashcode
判断hashcode
判断equals
2
可以看出,Java运行时环境会调用new A()这个对象的hashcode()方法。其中:
打印出的第一行“判断hashcode”是第一次map.put(new A(), new Object())所打印出的。
接下来的“判断hashcode”和“判断equals”是第二次map.put(new A(), new Object())所打印出来的。
那么为什么会是这样一个打印结果呢?我是这样分析的:
1、当第一次map.put(new A(), new Object())的时候,Java运行时环境就会判断这个map里面有没有和现在添加的 new A()对象相同的键,判断方法:调用new A()对象的hashcode()方法,判断map中当前是不是存在和new A()对象相同的HashCode。显然,这时候没有相同的,因为这个map中都还没有东西。所以这时候hashcode不相等,则没有必要再调用 equals(Object obj)方法了。参见推论4(如果两个对象hashcode不相等,他们一定不equals)
2、当第二次map.put(new A(), new Object())的时候,Java运行时环境再次判断,这时候发现了map中有两个相同的hashcode(因为我重写了A类的hashcode()方 法永远都返回1),所以有必要调用equals(Object obj)方法进行判断了。参见推论3(如果两个对象hashcode相等,他们不一定equals),然后发现两个对象不equals(因为我重写了equals(Object obj)方法,永远都返回false)。
3、这时候判断结束,判断结果:两次存入的对象不是相同的对象。所以最后打印map的长度的时候显示结果是:2。
改写程序如下:
import java.util.HashMap;
import java.util.Map;
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判断equals");
return true;
}
@Override
public int hashCode() {
System.out.println("判断hashcode");
return 1;
}
}
public class Test {
public static void main(String[] args) {
Map<A,Object> map = new HashMap<A, Object>();
map.put(new A(), new Object());
map.put(new A(), new Object());
System.out.println(map.size());
}
}
运行之后打印结果是:
判断hashcode
判断hashcode
判断equals
1
显然这时候map的长度已经变成1了,因为Java运行时环境认为存入了两个相同的对象。原因可根据上述分析方式进行分析。
以上分析的是HashMap,其实HashSet的底层本身就是通过HashMap来实现的,所以他的判断原理和HashMap是一样的,也是先判断hashcode再判断equals。
所以:写程序的时候应尽可能的按规范来,不然在不知不觉中就埋下了bug!
相关推荐
反之,如果两个对象的Hashcode相同,那么这两个对象不一定是相等的。 Hashcode只是一个必要条件,而不是充分条件。只有当两个对象的Hashcode相同,并且equals方法返回true时,这两个对象才能被认为是相等的。 ...
标题提到的"HashCode相同equals不同的2位字符集合算法"涉及到的是一个特定场景:两个对象可能具有相同的哈希码(hashCode),但并不意味着它们相等(equals返回true)。这个问题在处理自定义类对象时尤为关键,特别...
例如,如果我们有两个对象,它们的 hashCode 都是 1,那么它们将会被存放在同一个位置上。如果我们不定义 equals() 方法,那么这两个对象将被认为是同一个对象。 结论 hashCode 是一种非常重要的机制,在查找和...
1,如果两个对象相同,那么它们的hashCode值一定要相同; 2,如果两个对象的hashCode相同,它们并不一定相同 上面说的对象相同指的是用eqauls方法比较。 3,HashCode码不唯一
在 Java 中,如果两个对象相同,那么它们的 hashCode 值一定相同;但是,如果两个对象的 hashCode 值相同,它们并不一定相同。这个关系是 Java 中一个非常重要的设计原则,因为它可以确保对象的唯一性和快速查找。 ...
- 同等性原则:如果两个对象相等(根据 `equals()` 方法判断),那么它们的 `HashCode` 必须相同。 - 一致性原则:同一个对象在不同时间调用 `hashCode()` 方法应该返回相同的值,除非对象的状态发生改变。 #### ...
通常情况下,hashcode 方法需要和 equals 方法一起使用,以确保两个对象的散列码相同,如果它们的内容相同。 例如,在 HashSet 中,我们可以使用 hashcode 方法来比较两个对象是否相同。如果两个对象的散列码相同,...
重写 `hashCode()` 方法时,我们需要确保当两个对象满足 `equals()` 的条件时,它们的哈希码也相同。同时,为了提高性能,我们应该尽量让不同的对象产生不同的哈希码。例如,对于上述Person类,可以这样重写 `...
如果两个对象的内存地址相同,那么它们就是相等的。public boolean equals(Object obj) { return (this == obj); } 而在子类中,equals 方法的实现是比较两个对象的内容是否相等。例如,在 String 类中,equals ...
- 如果两个对象的 `hashCode()` 相同,尽管这可能导致哈希冲突,但是通过良好的设计和优化,依然可以保持高效的性能。 2. **解决哈希冲突**: - 哈希冲突是指不同的对象映射到了相同的哈希值上。为了避免过多的...
如果两个对象是相等的,那么它们必须有相同的哈希码值。 hashCode() 和 equals() 之间的关系 hashCode() 和 equals() 方法之间存在着非常重要的关系。如果两个对象是相等的,那么它们必须有相同的哈希码值。否则,...
反之,如果两个对象的`equals()`方法返回`false`,那么它们的`hashCode()`值也应该不同。这是Java Object类中的约定,也是所有自定义类应该遵循的规则。违反这个约定可能会导致哈希表的行为异常,例如,预期的查找...
如果两个对象的`hashCode`相同,则会进一步调用`equals`方法来判断这两个对象是否相等,避免重复插入相同的元素。 #### 4. `hashCode`与`equals`的关系 通常情况下,如果两个对象相等(即`equals`方法返回`true`)...
2. 如果两个对象不相同,那么它们的 hashCode 值可以相同,但是如果两个对象相同,那么它们的 hashCode 值一定相同。 通过这两个规范,我们可以得出以下结论: 1. 如果两个对象 equals,那么它们的 hashCode 值...
2. **非一致性**:如果两个对象的`hashCode`值不同,这并不意味着它们一定不相等。即`hashCode`值相同或不同不能作为判断两个对象是否相等的唯一依据。 3. **效率问题**:在实现`equals`和`hashCode`方法时,应当...
- 相反,如果两个对象的`hashCode()`值相同,并不能保证这两个对象通过`equals()`方法比较也是相等的。这是因为可能存在不同的对象具有相同的哈希值,这种情况称为哈希冲突。 - 为了避免哈希冲突带来的性能问题,在...
由于`a`和`b`的`hashCode()`相同,`HashSet`会假设这两个对象相等,但实际上它们不相等(因为没有重写`equals()`)。这导致了`HashSet`中包含两个看似相同的对象,违反了其设计原则,即不允许重复元素。 为了避免...
如果两个对象不相等,那么它们的哈希码不一定不同,但不同的对象应尽可能产生不同的哈希码,以提高哈希表的性能。 在重写 `hashCode()` 时,通常会基于 `equals()` 方法中用于比较对象相等性的属性来计算哈希码。...
对于集合类如`HashSet`来说,如果两个对象的`hashCode()`值不同,则可以认为这两个对象不相等;反之,如果两个对象的`hashCode()`值相同,则还需要进一步通过`equals()`方法来确定这两个对象是否真正相等。 #### ...