equals与==运用和区别(深入源码理解)

equals与==运用和区别(深入源码理解)

文章目录

双等号(==)

双等号比较的两种情况

对于==号只需要考虑两种情况:

  1. 基本数据类型使用==,比较的是值
  2. 引用数据类型使用==,比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。

源码和例子解析

Integer和Integer,Integer和int之间的比较

    Integer a1=new Integer(4321);
    Integer a2=new Integer(4321);
    int a3=4321;
    System.out.println("a1==a2:"+(a1==a2));//结果为false,因为a1,a2是两个不同的对象,其堆中的地址值自然不同
    System.out.println("a1==a3:"+(a1==a3));//结果为true,因为Java的自动拆箱机制,比较的是两个数值
    System.out.println("a3==a1:"+(a3==a1));//结果为false,原因同上
    Integer a4=4321;
    Integer a5=4321;
    /**
     * Integer a4=4321;
     * Integer a5=4321;
     * 相当于执行了
     * Integer a4=Integer.valueOf (4321) ;
     * Integer a5=Integer.valueOf (4321) ;
     */
    System.out.println("a4==a5:"+(a5==a4));//结果为false,相当于创建了两个对象,其地址自然不同
    
    Integer a6=100;
    Integer a7=100;
    /**
     * 为什么这个又不一样呢
     * 要根据装箱操作的源码
     * public static Integer valueOf(int i) {
     *         if (i >= -128 && i <= 127)
     *             return IntegerCache.cache[i + 127];
     *             //如果i的值大于-128小于127则返回一个缓冲区中已存在的一个Integer对象
     *         return new Integer(i);
     *         //否则返回 new 一个Integer 对象
     * }
     * 可以看出如果是-128-127之间,装箱装的是同一个对象(为了节约内存)
     *所以如果是-128到127之间的装箱操作,装的都是同样的对象!
     */
    System.out.println("a6==a7:"+(a6==a7));//为true
    Integer a8=128;
    Integer a9=128;
    System.out.println("a8==a9:"+(a8==a9));//为false
    Integer a10=-128;
    Integer a11=-128;
    System.out.println("a10==a11:"+(a10==a11));//为true
    a10=127;
    a11=127;
    System.out.println("a10==a11:"+(a10==a11));//为true
    /**
     * 注意,这种是直接生成对象的!
     * 没有进行装箱操作得到地址值,而是直接在堆创建生成的地址!
     * 所有不同的
     */
    Integer aa1=new Integer(1);
    Integer aa2=new Integer(1);
    System.out.println("aa1==aa2:"+(aa1==aa2));//为false

Java自动装箱拆箱机制的类型

equals与==运用和区别(深入源码理解)

String中==的解析

    String s1="12345";
    String s2="12345";
    String s3=new String("12345");
    String s4=new String("12345");
    /**
     * String s1=="xxx"这种字符串创建方式,并不是在堆中创建
     * 为了节省空间,是在运行时常量池,也就是在方法区中创建
     * s1和s2指向的是同一个地址
     */
    System.out.println("s1==s2:"+(s1==s2));//true
    System.out.println("s1==s3:"+(s1==s3));//false,s1指向的是常量池的地址,s3指向的堆中的地址
    System.out.println("s3==s4:"+(s3==s4));//false,s3和s4指向的是堆中不同的地址,所以false

equals

用法概述

equals没有被重写就是比较地址,相当于===

原因:
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。

equals源码
显然java源码对String类进行了特殊处理,String的equals以及不是比较地址值了,而是比较字符串的内容了。

equals与==运用和区别(深入源码理解)

equals与==运用和区别(深入源码理解)

  1. 首先两个对象在堆里面,地址值就不可能相等,所以要比较两个对象内容而不是地址时,必然要重写equals方法,比较内容
  2. hashCode默认情况下表示是对象的存储地址,如果两个东西相同,那么作为HashMap的key时应该是一样的,所以既然相等的规则变化了,自然要重写hashcode方法,跟随变化!

HashSet的equals

HashSet本身就重写了equals!

equals与==运用和区别(深入源码理解)

    HashSet<Integer> set1=new HashSet();
    HashSet<Integer> set2=new HashSet();
    set1.add(1);
    set1.add(2);
    set2.add(1);
    set2.add(2);
    System.out.println(set1.equals(set2));//返回true!

而且比较有意义的是
HashSet底层实现是通过HashMap实现的
equals与==运用和区别(深入源码理解)

数组的equals

Array的equals用的是原生的

equals与==运用和区别(深入源码理解)

equals与==运用和区别(深入源码理解)

    Integer[] array1={1,2,3};
    Integer[] array2={1,2,3};
    System.out.println(array1.equals(array2));//false
    
    //java.util.Arrays为我们提供了hashCode和euqals
    System.out.println(Arrays.equals(array1,array2));//true

但是java.util.Arrays为我们提供了hashCode和euqals,比较的是数组中的内部元素。

equals与==运用和区别(深入源码理解)

equals与==运用和区别(深入源码理解)
ps启发:
我们也可以学习这种方式
可以在不重写的方法下写一个通用类其中的方法进行计算hashcode和equals!

JAVA中重写equals()方法为什么要重写hashcode()方法

为什么重载hashCode方法?
答案:一般的地方不需要重载hashCode,只有当类需要放在HashTable、HashMap、HashSet等等hash结构的集合时才会 重载hashCode,那么为什么要重载hashCode呢?就HashMap来说,好比HashMap就是一个大内存块,里面有很多小内存块,小内存块 里面是一系列的对象,可以利用hashCode来查找小内存块hashCode%size(小内存块数量),所以当equal相等时,hashCode必 须相等,而且如果是object对象,必须重载hashCode和equal方法。

例如我们有对象A和对象B,现在重写了equals方法,让两者相等了,那我们用散列表HashSet存储了对象A,然后用set.contains(B),是查不到B存在的!

对象重写equlas和hashcode实例

package com.company;

public class Main {
    public static void main(String[] args) {
	// write your code here
        Person p1=new Person("l1",11,"男");
        Person p2=new Person("l1",11,"男");
        Person p3=new Person("l1",12,"男");
        System.out.println(p1==p2);//false
        System.out.println(p1.equals(p2));//true
        System.out.println(p1.equals(p3));//false
    }
}


class Person{
    String name;
    Integer age;
    String sex;
    @Override
    public int hashCode() {
        return 31*(this.name.hashCode()+31*this.age.hashCode()+31*(this.sex.hashCode()));
    }

    @Override
    public String toString() {
        return this.name+"  "+this.age+"  "+this.sex;
    }

    @Override
    public boolean equals(Object obj) {
        Person person1=(Person) obj;
        return this.hashCode()==person1.hashCode();
    }

    public Person() {
    }

    public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}

outPut

false
true
false
Process finished with exit code 0

Ps: 如果要用equal方法,请用object<不可能为空>.equal(object<可能为空>))
"target".equals(foo)

上一篇:自动装箱 与 拆箱


下一篇:在单链表的第i个位置后插入一个节点(阿里+腾讯等面试题总结)