关于重写hashCode和equals方法的一些思考

  经常能看到重写equals方法就需要重写hashCode方法的说法,这点也很好理解,假如重写equals使得两个对象通过equals判断为真 ,但是如果hashCode计算出来的值如果不一样,就会发生矛盾,就是明明两个对象是一样的,但是却会被映射到不同位置,这样子的话,hashMap或者hashSet之类的哈希结构就会存储多个相同的对象。还可以通过一个例子理解

		Map<String,Value> map1 = new HashMap<String,Value>();
        String s1 = new String("key");
        String s2 = new String("key");
        Value value = new Value(2);
        map1.put(s1, value);
        System.out.println("s1.equals(s2):"+s1.equals(s2));
        System.out.println("map1.get(s1):"+map1.get(s1));
        System.out.println("map1.get(s2):"+map1.get(s2));


        Map<Key,Value> map2 = new HashMap<Key,Value>();
        Key k1 = new Key("A");
        Key k2 = new Key("A");
        map2.put(k1, value);
        System.out.println("k1.equals(k2):"+k1.equals(k2));
        System.out.println("map2.get(k1):"+map2.get(k1));
        System.out.println("map2.get(k2):"+map2.get(k2));

  Key和Value的类定义如下

static class Key{
        private String k;
        public Key(String key){
            this.k=key;
        }
        //如果不重写hashCode,只重写了equals,会造成相同值被放入不同的桶中
//        @Override
//        public int hashCode() {
//            return k.hashCode();
//        }

        @Override
        public boolean equals(Object obj) {
            if(obj instanceof Key){
                Key key=(Key)obj;
                return k.equals(key.k);
            }
            return false;
        }
    }
    
 static class Value{
        private int v;

        public Value(int v){
            this.v=v;
        }

        @Override
        public String toString() {
            return "类Value的值-->"+v;
        }
    }

  输出结果如下
在这里插入图片描述
  可以看出,如果重写了equals但不重写hashCode的话,会出现相同的对象会被map判断成不同对象,导致可以重复插入多个相同对象。
  除此之外,还会思考如果重写hashCode但不重写equals方法的情况下,又会造成什么问题,因此用以下例子说明

		Map<Integer, Integer> map3 =new HashMap();
        while (true){
            boolean flag = false;
            for (int i = 0; i < 1000; i++) {
                if(!map3.containsKey(i)){
                    map3.put(i, i);
                    flag = true;
                }
            }
            if (flag == false) {
                break;
            }
            System.out.println("map3的容量" + map3.size());
        }

        Map<Key2, Integer> map4 =new HashMap();
        while (true){
            boolean flag = false;
            for (int i = 0; i < 1000; i++) {
                if(!map4.containsKey(new Key2(i))){
                    map4.put(new Key2(i), i);
                    flag = true;
                }
            }
            if (flag == false) {
                break;
            }
            System.out.println("map4的容量" + map4.size());
        }

  Key2的类定义如下

    static class Key2{
        Integer id;

        Key2(Integer id) {
            this.id = id;
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }
        //不重写equals就会导致一直认为没有相同的值,就会一直插入。
//        @Override
//        public boolean equals(Object obj) {
//            if(obj instanceof Key2){
//                Key2 key2 =(Key2)obj;
//                return id.equals(key2.id);
//            }
//            return false;
//        }
    }

  结果如下
在这里插入图片描述
  从图中结果可以看出,map4一直在添加数据,说明map一直认为没有相同的key对象,因此对于同一个i,不重写的equals永远不会判断相同,所以会一直插入。因此hashCode和equals必须全部重写,任何一个不重写都会发生错误。
  到这里也还会思考,String和Integer的和hashCode和equals是怎么计算的
  Integer的hashCode计算如下
在这里插入图片描述
  可以看出是直接返回原始值
  String的hashCode计算如下
在这里插入图片描述
  可以由注释看出来,计算的结果就是s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1],就比如字符串“abc”,a的ascll码是97,b是98,c是99,因此该字符串的hashCode值就是(97 *31 + 98)*31 + 99,这里引出一点思考:为什么用31呢?,查阅资料得知因为31是一个质数,可以使得减少哈希算法的冲突概率,同时31的二进制数是11111,因此31 *i就等于(i << 5) - i,可以优化运算。
  Integer的equals计算如下
在这里插入图片描述
  就是说明,只需要使用instanceof判断传入对象是否是Integer的实例或者子类,是就强转成Integer类,然后判断值是否相等
  String的equals计算如下
在这里插入图片描述
  这里首先用“==”比较了equals两边对象,如果一样直接返回true,然后就是用instanceof判断是否是String实例或者子类,如果是就强转,然后再根据数组长度判断是否相同,如果相同就遍历数组每个元素,都相同就返回true,其他情况都返回false。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值