面试问题/十进制的存储-小数无法精确-equals/hashCode的重写

1-请你讲讲一个十进制的数在内存中是怎么存的?

以二进制补码形式存储,最高位是符号位(正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,符号位为1,其他位取反)
如:比如说-10,假设就存八位,先变成原码就是00001010,反码是 11110101,加1后变为补码 11110110。时刻要记得最高位代表符号

[正数的补码反码是其本身,负数的反码是符号位保持不变,其余位取反]
[正数的补码是其本身,负数的补码是在其反码的基础上+1]

+7的原码:0000 0111 
-7的原码:1000 0111   第一位0代表正数,1代表负数,第一位为符号位

+7的反码:0000 0111 正数反码和原码一样
-7的反码:1111 1000  符号位不变,其他取反

+7的补码:0000 0111 正数补码和原码一样
-7的补码:1111 1001 十六进制为:F9 负数的补码原码取反再加一
正数:原码,反码,补码,正数都是一样的
负数:原码取反(不包括符号位)+1 = 补码
参考了(https://blog.csdn.net/weixin_30340819/article/details/96448992)

2- 为什么1-0.9无法精确计算

public class Main {
    public static void main(String[] args) {
        double x = 1.0 / 10;
        double y = 1 - 9.0 / 10;
        // 观察x和y是否相等:
        System.out.println(x);
        System.out.println(y);
    }
}

在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。
浮点数0.1在计算机中就无法精确表示,因为十进制的0.1换算成二进制是一个无限循环小数,很显然,无论使用float还是double,都只能存储一个0.1的近似值。但是,0.5(0.1)这个浮点数又可以精确地表示。
在这里插入图片描述即某个十进制数可以通过乘以2^n=等于1的,就可以转换为二进制表示。

0.03125二进制表示就是0.00001.
0.625
0.625*2=1.25         1
0.25*2 =0.5          0
0.5*2 = 1            1
由上向下:0.625二进制表示就是0.101

二进制转十进制:
在这里插入图片描述

3-请你解释为什么重写equals还要重写hashcode?

(知识点:link
原解答:

HashMap中,如果要比较key是否相等,要同时使用这两个函数!因为自定义的类的hashCode()方法继承于Object类,其hashCode码为默认的内存地址,这样即便有相同含义的两个对象,比较也是不相等的。HashMap中的比较key是这样的,先求出key的hashCode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。如果只重写hashCode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素 是否相等。重载hashCode()是为了对同一个key,能得到相同的HashCode,这样HashMap就可以定位到我们指定的key上。重载equals()是为了向HashMap表明当前对象和key上所保存的对象是相等的,这样我们才真正地获得了这个key所对应的这个键值对。

理解:
两个知识点:

  1. object类中的equals方法是判断两个引用是否指向堆内存中的同一块地址
  2. object类中的hashCode方法是根据内存地址计算出hash值

可以看到,如果一个对象/引用是指向同一块地址的话,他们计算出来的hashCode也是相同的。如下:

String a = "VV";
String b = "VV";
System.out.println(a.hashCode());
System.out.println(b.hashCode());
System.out.println(b.equals(a));
'输出:'
2252
2252
true

Object.hashCode的通用约定:
1、在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回 同一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。

2、如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。

3、如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表(hash table)的性能。

因此,在使用hashmap时候,使用了对象当作关键字,当因为业务需求需要重写equals的时候,(如下面代码)当G属性相等的时候就认为两个对象相等,但是如果这个时候如果没有重写hashCode的话,会因为存放的地址不同,而判断对象不相等。hashCode就会导致不能正常取出对象对应的元素

package collection;

import java.util.HashMap;

public class HashMapTest{
    protected int G;
    protected String name;
    public HashMapTest(int G,String name){
        this.G = G;
        this.name = name;
    }
    @Override
    public boolean equals(Object obj) {
        return (this.G == ((HashMapTest)obj).G);
    }
    public static void main(String[] args) {
        HashMap<HashMapTest,String> dictionary = new HashMap<>();
        dictionary.put(new HashMapTest(1,"teemo1"),"提莫");
        System.out.println("重写equals但是不重写hashCode---->"+dictionary.get(new HashMapTest(1,"teemo2")));
    }
}
'输出:'重写equals但是不重写hashCode---->null

重写equals同时重写hashCode,就可以正常取出,而且从代码可以看出,Map在比较关键字时候都会取出他们的hashCode,进行比较,相同的话,再调用equals方法比较。

package collection;

import java.util.HashMap;

public class HashMapTest{
    protected int G;
    protected String name;
    public HashMapTest(int G,String name){
        this.G = G;
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        return (this.G == ((HashMapTest)obj).G);
    }
    public int hashCode(){
        System.out.println(this.name+",调用了hashCode");
        return this.G;
    }

    public static void main(String[] args) {
        HashMap<HashMapTest,String> dictionary = new HashMap<>();
        dictionary.put(new HashMapTest(1,"teemo1"),"提莫");
        System.out.println("重写equal同时重写hashCode---->"+dictionary.get(new HashMapTest(1,"teemo2")));
    }
}
'输出:'
teemo1,调用了hashCode
teemo2,调用了hashCode
重写equals同时重写hashCode---->提莫

参考了文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值