zoukankan      html  css  js  c++  java
  • Java核心(六):==和equals()的区别;重写equals()方法

    一、对字符串而言,==和equals()的区别
    • "==" 比较的是两个对象的引用(内存地址)是否相同,也用来比较两个基本数据类型的变量值是否相等。
    • equals() 比较的是两个对象的值(内容)是否相同。

    二、==和equals()的区别

    • 对于==:在简单类型中(int等),这能使用该方法进行比较,这种类型没有equals方法,int的值是存在栈中的,==比较的是栈的内容是否相同。在String类型中,比较特殊,用String=“****”;这种进行赋值时,两个相同的值用==比较也是相同的。但是用new String(),赋值就不相同。说明String=“”时,java会检查在堆中是否由相同的值,如果有,把新对象的地址也同老对象的地址赋为相同,因此==比较会相同(“****”存储在常量区内存中)。但是new String()开辟的就是两个栈,因此用==比较不会相同。对于包装类,如Integer num=127;时,进行自动装箱操作。如果数值在-128-127会有缓存,此时==是相同的;如果数值不在-128~127之间,则==不相同。
    • 对于equals:当时String类型或者是包装类(如Integer),比较的就是堆中的值。对于用户自定义的普通类,equals比较的内存的首地址,这时候和==是一样的,即比较两边指向的是不是同一个对象。

    三、为什么重写了equals方法时,都要进而重写Hashcode方法呢?

      原因如下:当equals此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:

      (1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true

      (2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false

      hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。

      这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致。

    四、如何重写equals()方法?

    package com.study;
    
    import java.util.Date;
    import java.util.Objects;
    
    public class Employee {
    
        private String name;
        private double salary;
        private Date hireDay;
    
        public Employee(String name, double salary, Date hireDay) {
            this.name = name;
            this.salary = salary;
            this.hireDay = hireDay;
        }
    
        public Employee() {
        }
    
        /**
         * 重写equals()方法
         *
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            // 如果为同一对象的不同引用,则相同
            if (this == obj) {
                return true;
            }
            // 如果传入的对象为空,则返回false
            if (obj == null) {
                return false;
            }
    
            // 如果两者属于不同的类型,不能相等
            if (getClass() != obj.getClass()) {
                return false;
            }
    
            // 类型相同, 比较内容是否相同
            Employee other = (Employee) obj;
            return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
        }
    
    
    }
    
    /**
     * 测试类
     */
    class Test {
        public static void main(String[] args) {
            Date date = new Date();
            Employee e1 = new Employee("zhangsan", 100, date);
            Employee e2 = new Employee("zhangsan", 100, date);
            System.out.println(e1.equals(e2));
    
        }
    }

    五、重写hashCode()方法

    1、推荐阅读

    2、如何重写hashcode方法;

    • String对象和Bigdecimal对象已经重写了hashcode方法,这些类型的值可以直接用于重写hashcode方法;
    • result = 31 *result + (dishCode !=null ?dishCode.hashCode() : 0);,这里面为啥用个31来计算,而且很多人都是这么写的,这是因为31是个神奇的数字,任何数n*31都可以被jvm优化为(n<<5)-n,移位和减法的操作效率比乘法的操作效率高很多。

    3、这边再拷贝下别人说的比较经典的总结:

    Google首席Java架构师Joshua Bloch在他的著作《Effective Java》中提出了一种简单通用的hashCode算法

    1. 初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;

    2. 选取equals方法中用于比较的所有域,然后针对每个域的属性进行计算:

      (1) 如果是boolean值,则计算f ? 1:0

      (2) 如果是bytecharshortint,则计算(int)f

      (3) 如果是long值,则计算(int)(f ^ (f >>> 32))

      (4) 如果是float值,则计算Float.floatToIntBits(f)

      (5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int

      (6)如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。  否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode值为0

      (7)如果是数组,那么需要为每个元素当做单独的域来处理。如果你使用的是1.5及以上版本的JDK,那么没必要自己去    重新遍历一遍数组,java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上,


    4、关于hashcode,我们一定要知道一个口诀:

    • hashcode相等,两个对象不一定相等,需要通过equals方法进一步判断;
    • hashcode不相等,两个对象一定不相等;
    • equals方法为true,则hashcode肯定一样;
    • equals方法为false,则hashcode不一定不一样;
  • 相关阅读:
    Java实现AES加密
    spring定时任务详解(@Scheduled注解)
    springBoot 项目war包部署及改为war包后资源路径错误问题
    (转)如何在maven的pom.xml中添加本地jar包
    HttpClient MultipartEntityBuilder 上传文件
    Java BigDecimal详解
    mysq带条件的分页查询数据结果错误
    jstack生成的Thread Dump日志线程 分析
    jquery将表单序列化
    java jdk动态代理学习记录
  • 原文地址:https://www.cnblogs.com/newbie27/p/10426846.html
Copyright © 2011-2022 走看看