查看原文
其他

SO面试题07:使用equlas和hashcode方法时,我们需要考虑那些问题?

忆蓉之心 Java面试那些事儿 2020-10-08


# 问题


当我们使用equals和hashcode方法时,我们需要考虑什么问题?


# 最佳答案


就像《effective java》这本书中提到,尽管java.lang.Object是一个具体的类,但是这个类的主要功能就是用于扩展(非final方法可被覆盖),因此,它所有的非final方法都有明确的约定。所有的类在覆盖Object的方式时,都有义务遵守这些约定。否则,其它依赖这些约定的类就无法正常运转,比如我们经常提到的HashMap、HashSet等等。


接着,咱们看看本题目中所提到的两个方法:


hashCode()


我们可以主要到这个方法由native关键字修饰,因此,它底层由c/c++来实现(符合JNI编程规范),关于具体的实现,这里不做过多的讨论,感兴趣的可以去看看openjdk源码。


同时,javadoc文档中提到:如果一个对象提供给equals做比较的信息没有被修改的话,该对象多次调用hashCode()方法,该方法必须始终如一返回同一个integer。


equals()


从Object的源码中,我们可以看出它是对两个对象的地址值进行的比较(即比较引用是否相同)。如果你看过String 、Math、Integer、Double等这些包装类的equals()方法源码时,会发现,它们已经覆盖了object类的equals()方法。


它们已不再符合地址比较,它们之间符合等价关系(自反性,对等性,传递性,一致性),另外,它们必须是一致的(如果未修改对象,则它必须保持返回相同的值)。此外,o.equals(null)必须始终返回false。


因此,这两个方法之间的关系,应该是:

如果两个对象相同,那么它们的hashCode值一定要相同;


如果两个对象的hashCode相同,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)。   


equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

Object规范


这里需要再次提醒一下,也是在《effective java》这本书(见第9条)中曾提到过,也是本题的答案:equal和hashcode方法必须同时重写,缺一不可


怎么重写呢?


使用类的同组属性去重写equals和hashcode方法。


这里,第三方的组件commons-lang3给我们提供了两个工具类,分别是 EqualsBuilder 和 HashCodeBuilder方法,见下面的例子:

public class Person { private String name; private int age; // ...
@Override public int hashCode() { return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers // if deriving: appendSuper(super.hashCode()). append(name). append(age). toHashCode(); }
@Override public boolean equals(Object obj) { if (!(obj instanceof Person)) return false; if (obj == this) return true;
Person rhs = (Person) obj; return new EqualsBuilder(). // if deriving: appendSuper(super.equals(obj)). append(name, rhs.name). append(age, rhs.age). isEquals(); }}

记住,使用基于hashcode的Collection或Map(例如HashSet,LinkedHashSet,HashMap,Hashtable或WeakHashMap)时,请确保放入元素(key)的hashcode在集合中一直保持不变。


我记得之前有个同学,跟我提过,他在使用hibernate的过程中,发现hibernate经常会使用set集合来保持相关对象。他有次只重写了equals方法,而没有重写hashcode方法,当存入的元素越来越多时,发现性能越来越差,为啥呢?


后来,跟踪发现,原来是set在进行判重时,会有大量的元素的hashcode方法返回的值一样,因此会调用equals方法进行比较,降低了性能。重写了hashcode方法,就好了。


比如,这样写:

public int hashCode(){ return 1; //等价于hashcode无效

你可以去尝试一下。


今晚,就写到这里,记得转发,给我补充下一直写下去的燃料~~~


参考:http : //stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java



 往期推荐 

🔗





点击阅读原文,获得更多精彩内容

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存