1. public native int hashCode();
    2. public boolean equals(Object obj) {
    3. return (this == obj);
    4. }

    这是Object类的equals和hashcode方法
    hashcode是native修饰也就是C来实现的,其实就是取对象在内存里的地址
    equals采用了’==’操作,我们都知道这是针对对象地址进行比较。那么这里天然满足:

    • equals=true,hashcode一定相等,因为地址都一样,而hashcode取的就是地址。同理:
    • equals=false,hashcode一定不相等。

    扩展:那为什么基本类型和字符串(也不一定)可以比实际值呢?是因为java内部对==运算符进行了重载,但这个功能没有开放出来,比如C++就提供了对运算符进行重载的能力。

    你会问:我为什么要重写equals?_

    很显然,地址相等就是指向同一个对象,那么两个句柄肯定相等,但这个条件太苛刻了,实际开发中,很多情况就是两个堆上分配的不同对象进行比较,比如一个Stu类,有一个学号sid,我们业务上认为sid相同,这两个对象就是相等的,那么可以重写equals,因为是基于业务条件来判断相等,那么会出现:

    • equals=true/fasle,hashcode都不相等。因为堆上地址不相等。

    扩展:实际工作中真的碰到这种情况,我们也不会这么做,而是直接比较两个对象的sid,一般sid都是数字或者字符串,根据上面的重载理论就可以天然支持,那么可以省去重写equals的工作。


    你会问:hashcode不相等就不相等呗,我又没用到hashcode?
    _
    你说的没错,你不重写hashcode,jre既不会在编译,也不会在运行时抛异常,但你需要确保:

    1. 确实没有用到,不只是没有显示用到,包括被用于以哈希表为基础的jdk提供的集合框架的key,比如hashmap、hashset、linkedhashmap等等。
    2. 所有地方没有用到,你自己没用到,其他一起开发的人没用到,打出去的jar包别人工程里也没用到。

    扩展:实际工作中真的碰到这种情况,99%以上的情况不会用自定义类作为key。至少我目前都没见过。90%以上都是用的string,其他可能用的各个基本类型的封装类。因为他们都已经对__equals和hashcode做了优化。
    _
    与其这么麻烦,还不如重写呢是不是!重写简单吗,说简单也不简单。要满足这种规则:

    • equals=true,hashcode一定要相等。
    • equals=false,hashcode尽量保证不相等。

    为什么是尽量保证不相等,而不是一定要求不相等。因为做不到,这要从哈希算法角度来讲,稍微了解hash算法的同学都知道,hash算法是一个摘要算法,也就是输入任意一个内容,输出去一个int值。int值最大是多少:2 ^ 31 - 1大概20多亿吧。但是现实世界中的内容是无穷无尽的,假设对有30亿个内容输入,你告诉我怎么办?所以哈希冲突不可避免。

    扩展:从另一个角度来理解,hash是一个无限集合到有限集合的映射关系,所以一定会存在溢出或冲突的问题。

    你会问:为什么要去尽量保证hashcode不等?

    还是那句话,你要是没用于哈希表,连重写都不用,更别说保证。
    假设你要用于哈希表,那么我们要来先聊聊哈希表,也叫散列表。是一种常用的数据结构,详见哈希表
    你可以找到一个关键语句:哈希表可以提高查找效率。
    基于这个前提,如果不保证不等,最差情况比如直接返回一个常数,也就是所有内容的hashcode都相等。
    哈希冲突怎么办?

    • 开放寻址:就是按顺序去寻找下一个(顺序或随机)空位置,适用于稀疏的哈希表,会增加后续冲突的概率。
    • 重哈希:因为int就那么大,再来一次最多和当前这个不冲突,但是还是会增加后续冲突的概率。
    • 链地址:遇事不决加一层,加一层链表,把冲突的地址链在头结点的后面。
    • 建立公共溢出区:遇事不决加一层,加一层数组,冲突都放在数组里。

    我们先不管这些冲突解决方案的实现思想,我直接给出结论:不管哪一种方案都会降低哈希表的查找性能。
    所以说要尽量保证hashcode不等。

    你会问:为什么重写equals一定要重写hashcode?

    因为默认的equals是地址相等才为true,也就是最严苛的情况,而此时hashcode也一定能保证相等。
    所以任何对equals的重写都只会导致条件变宽,如果只对equals重写而不重写hashcode,可就可能出现:equals=true但hashcode不等的情况。
    在这种情况下,如果作为hashmap的key,就会出现两个equals=true的key却都可以put到hashmap中。而且也不能通过get获取到值。列子如下:

    1. static class UserKey{
    2. private String uid;
    3. private String name;
    4. public UserKey(String uid, String name) {
    5. this.uid = uid;
    6. this.name = name;
    7. }
    8. @Override
    9. public boolean equals(Object o) {
    10. if (this == o) { return true; }
    11. if (o == null || getClass() != o.getClass()) { return false; }
    12. UserKey userKey = (UserKey) o;
    13. return Objects.equals(uid, userKey.uid) &&
    14. Objects.equals(name, userKey.name);
    15. }
    16. @Override
    17. public int hashCode() {
    18. return Objects.hash(uid, name);
    19. }
    20. }
    21. public static void main(String[] args) {
    22. HashMap<UserKey, String> map = new HashMap<>();
    23. map.put(new UserKey("111", "zl"), "test_value_1");
    24. map.put(new UserKey("111", "zl"), "test_value_2");
    25. System.out.println(map.size());
    26. System.out.println(map.get(new UserKey("111", "zl")));
    27. }
    28. // 如果不重写hashcode,返回如下:
    29. 2
    30. null
    31. // 如果重写hashcode,返回如下:
    32. 1
    33. test_value_2


    以上です!!!**