JAVA: equals 思考

本编博文,我想分析回顾下 Java equals方法的相关内容,并由此对散列和内存分配进行几点总结与思考,防止自己以后再犯低级错误。特此铭记,共勉。


1. == 比较

== 永远是用来比较内存地址的

网上常见的表达形式为:”==” 用于比较引用和比较基本数据类型时具有不同的功能:

  • 基本类型:
    • 如果两个变量的值相等则返回 true;
  • 引用:
    • 如果两个引用指向内存中的同一个对象,也就是引用的值(内存地址)相等,则返回true;

这种说话不能说错,但是容易给我们这些新手造成学习上的迷惑,其实基本类型实际上还是在比较地址!

  • 基本类型及其包装类,Java 中只有8中基本的数据类型分别为:
  • byte (Byte);
  • short (Short);
  • int (Integer);
  • long (Long);
  • float (Float);
  • double (Double);
  • char (Character);
  • boolean (Boolean).
    - 注意String 不是基本类型;

要弄清上面的问题我们需要理解或者说了解一下几方面的内容:

  • 堆、栈、常量池问题(对于JVM内存的具体分析和反编译来看内存的分配过程,我目前彻底理解还做不到,等我学到了回头补充)

  • 基本类型变量在栈中是怎么分配与共享的;

  • String 类型变量和基本类型的包装类型变量在不使用new时,在常量池和栈中是怎么分配的,

  • 常量池技术

在此大致总结如下,

  • 栈:存放基本类型的数据和对象的引用, 这些基本类型的变量的值和基本类型引用变量都会在栈中,其次由 new 在堆中生成的对象的引用和实现常量池的 String 和一些基本类型包装类的引用都会存在栈中;并且栈中的引用变量存的都是相应内容的所在的内存地址值;并且局部变量的数据也存放在栈中;

  • 堆:存放用new产生的数据和没有实现或者不满足常量池技术条件的基本类型包装类;

  • String类型和Java的8种基本类型包装器(Byte, Short, Integer, Long, Character, Boolean, Float, Double), 除Float和Double以外, 其它六种都实现了常量池, 但是它们只在 [-128, 127] 时才使用常量池。而如果大于127 或小于-128 则不会使用常量池所以会直接在堆内存中创建对象。

    具体分析请参考JAVA: Java 堆 栈 常量池


2 equals比较

public boolean equals(Object obj)

其比较规则为:当参数obj引用的对象与当前对象为同一个对象时,就返回true,否则返回false. 由此可见,equals()方法的本意为确定两个对象的引用是否相同。

JDK类中有一些类覆盖了oject类的equals()方法,
比较规则为:如果两个对象的类型一致,并且内容一致,则返回true,这些类有:
java.io.file, java.time.LocalDate, java.lang.string, 包装类(Integer,Double等)等;

String str="hello";
String str2 = new String ("hello");
String str3 = new String (str);
System.out.println(str2==str3);//false
System.out.println(str2.equals(str3));//true

主要: String 类重写了equals方法,只有两个对象的内容相等就返回true;

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
Integer i1 = new Integer(300);
Integer i2 = new Integer(300);
System.out.println(i1==i2);//false
System.out.println(i1.equals(i2));//true        

基本类型的包装类也重写了 hashcode() 和equals 方法;

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
重写equals方法必须重写hashCode方法:

当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。

    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }

        if (obj == null) {
            return false;
        }

        if (this.getClass() != obj.getClass()) {
            return false;
        }

        People p = (People)obj;
        return this.name.equals(p.name) && this.age == p.age;
    }
    public int hashCode() {

       // TODO Auto-generated method stub
        return name.hashCode();

    }

最后引入一段写的挺好的开场白:浅谈java中的hashCode方法
对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。

  考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)

  也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值