• 比较两个值是否相等


    判断两个对象是否相等时,有两种方法,==equals()

    • == : 它的作用是判断两个对象的地址是不是相等。对于基本类型,比较的是值,对于对象,比较的是对象的存放地址。

    • equals() : 它的作用也是判断两个对象是否相等。

      • 如果类中覆盖了该方法,那么通常是比较两个对象的内容是否相等。如果相等,则返回 true;如果不等,则返回 false
      • 如果类中没有覆盖该方法,那么比较的是两个对象的地址是否相等,等价于==

    在举例子之前,先说一下基本类型和包装类型的东西。

    1. 基本类型和包装类型

    1.1 包装类型

    基本类型有8种,都有各自的默认值,默认值均不为null,分别是:

    • 6 种数字类型 :byteshortintlongfloatdouble

    • 1 种字符类型:char

    • 1 种布尔型:boolean

    1.2 包装类型

    包装类型是基本类型所对应的对象。由于包装类型是对象,所以可以用于泛型,

    1.3 信息总结

    这 8 种基本数据类型的默认值以及所占空间的大小如下:

    基本类型位数字节默认值对应的包装类型包装类的默认值
    数字型short1620Shortnull
    int3240Integernull
    long6480LLongnull
    字符型byte810Bytenull
    char162‘u0000’Characternull
    数字型float3240fFloatnull
    double6480dDoublenull
    布尔型boolean1FALSEBooleannull

    1.4 在哪里放着

    • 基本类型的局部变量在栈中的局部变量表中存放,基本类型的成员变量在堆中存放。

    • 包装类型是对象类型,几乎所有的对象类型都在中。

    Alt

    小纸条

    如果类中成员变量使用基本类型(如 int),并且不被static修饰的话,会存放在堆中。

    这种情况,使用基本类型对应的包装类型更好。

    class Student {
        private int age;
    }
    
    // 改为包装类型
    class Student {
        private Integer age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.5 包装类型的缓存机制

    包装类型会用缓存机制来提高性能。各个包装类型的缓存能数据范围如下:

    包装类型缓存数据范围
    数字型Short[ -128, 127 ]
    Integer[ -128, 127 ]
    Long[ -128, 127 ]
    字符型Byte[ -128, 127 ]
    Character[ 0, 127 ]
    布尔型Booleantrue / false

    如果要创建的包装类型的数据在缓存中能找到,那么会直接使用缓存中的数据,而不会创建新的对象。比如

    Integer i1 = 10;    // 10 < 127,i1使用缓存中的数据
    // 判断 i1 的情况
    // 如果 i1 使用缓存中的数据,没有创建新对象,那么再创建一个值为10的对象,
    // 两个对象的地址应该是相同的,都是缓存中的数据的地址
    Integer i12 = 10;
    System.out.println(i1 == i12);   // 输出结果为 true    
    
    Integer i2 = 128;    // 128 > 127,i2创建新的对象
    // 判断 i2 的情况
    // 如果 i2,创建了新对象,没有使用缓存中的数据,那么再创建一个值为128的对象,
    // 两个对象的地址应该是不同的,因为两个对象的地址不会相同
    Integer i22 = 128;
    System.out.println(i2 == i22);    // 输出结果为 false  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.6 自动拆箱与装箱

    自动装箱和拆箱,是自动在基本类型和包装类型之间进行转换,这种转换是在赋值、方法调用时等情况下发生的,比如

    // 基本类型 转为 包装类型
    Integer a = 200;   
    // 200是基本类型,但是可以直接赋给 Integer 变量a ,而不用进行类型转换
    // 如果没有自动装箱,就只能使用 Integer a = new Integer(200); 来创建变量
     
    // 基本类型 转为 包装类型
    int b = a;    // a 是包装类型,但是可以直接赋给int类型 ,而不用进行类型转换
    // a 是 Integer类型,b 是 int 类型,如果没有自动拆箱,就需要自己进行类型转换
    自动拆箱和装箱,是在本来需要自己进行类型转换的时候,交给Java进行,而不用自己强制转换。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. 比较

    • 基本类型和基本类型比较,使用 == 即可。

    • 基本类型和包装类型比较,使用 ==

    • 对象和对象比较,使用equal()

    2.1 数字比较

    基本类型和基本类型比较,比较的是值是否相等。

    int a = 10;
    int b = 10;    // b == a
    int c = new Integer(10);    // c == a
    
    • 1
    • 2
    • 3

    基本类型和包装类型比较,比较的也是值是否相等。

    int a = 10;
    Integer aa = new Integer(10);    // aa == a
    
    • 1
    • 2

    包装类型和包装类型比较,比较的是地址是否相等。

    Integer a = new Integer(10);
    Integer b = new Integer(10);    // b != a,不是同一个对象,地址不同
    
    • 1
    • 2

    2.2 String 比较

    String是一个常量,存放在运行时常量池(在方法区/元空间)。是不可变的(在创建之后无法更改),它的底层是由char[]实现的。比如:

    String str = "abc";
    // 等价于
    char[] data = {'a', 'b', 'c'};
    String str = new String(data);
    
    • 1
    • 2
    • 3
    • 4

    由于String对象是不可变的,所以它们是可以被共享的。在创建新的String对象时,如果常量池中存在该对象的值,那么不会创建新的字符串,而是会直接使用常量池中存在的字符串。如果常量池中不存在该对象的值,那么会在常量池中创建字符串。

    所以,比较两个类型为 String 的字符串是否相等,最好使用equals()

    // 使用常量池的数据
    String s1 = "abc";    // 在常量池中创建一个 abc 字符串
    String s2 = "abc";    // 使用常量池中的数据
    System.out.println(s1 == "abc");    // true。s1 和 "abc" 的地址相同
    System.out.println(s2 == "abc");    // true。s2 和 "abc" 的地址相同
    System.out.println(s1 == s2);    // true。s1 和 s2 指向的是同一个对象
    System.out.println(s1.equals(s2));    // true.s1 和 s3 的值相等
    
    // 使用 new 创建新对象
    String s3 = new String("abc");    // 创建一个新对象
    System.out.println(s3 == "abc");    // false。s3 和 "abc" 的地址相同,s3 使用的不是常量池中的数据
    System.out.println(s3 == s1);    // false。s1 和 s3 不是一个对象
    System.out.println(s1.equals(s3));    // true。s1 和 s3 的值相等
    
    String s4 = "def";    // 在常量池中创建一个 def 字符串
    System.out.println(s1 == s4);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    字符串和对象引用的示意图如下:
    字符串和对象引用

    2.3 对象比较

    比较两个对象的地址是否相同,如果相同则返回 true,如果不同则返回 false

    // 创建一个 Integer 对象
    Integer a = new Integer(10);
    // 使用两个变量作为对比
    Integer b = new Integer(10);    // b != a,不是同一个对象
    Integer c = a;    // c == a,是同一个对象
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对象和对象引用的示意图如下:
    字符串和对象引用

    3. HashMap相关

    由于 HashMap要说的内容和上面的比较情况稍有不同,这里把 HashMap中想要说的东西单独拎出来。

    1)containsKey()

    HashMap中常常需要判断某个值是否在HashMap中,会用到containsKey()。该方法判断某个值在HashMap中是使用equals()来判断的, 也就是说,如果一个类覆盖了这个方法,那么是并根据值来判断的;如果一个类没有覆盖这个方法,那么是根据地址来判断的。

    containsKey()有说到,key==null ? k==null : key.equals(k)(其中,keyHasMap中的键,k是要差值的值)当哈希表中键不为空并且等于其中一个键值时,返回true。简短的看,containsKey()就是使用 equal()方法比较keyk是否相等。

    举个String例子:

    Map map = new HashMap<>();
    map.put("a", 1);
    map.put("b", 2);
    map.put("c", 3);
    
    String s = new String("a");    // 创建一个对象
    // 比较的是s和map中的键值是否有相等的。因为String重写了 equals() 方法
    System.out.println(map.containsKey(s));  // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    举个一维数组的例子:

    Map map = new HashMap<>();
    map.put(new int[]{1, 2}, 1);
    map.put(new int[]{2, 3}, 2);
    map.put(new int[]{3, 4}, 2);
    
    int[] array = new int[]{1, 2};    // 创建了一个新数组
    // 比较的是array和map中的键值的地址是否有相等的。因为 int[]没有重写 equals()方法
    System.out.println(map.containsKey(array));    // false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    小纸条

    如果需要比较两个数组是否相等时,需要使用 Arrays类的 equals()方法,比如

    int[] array1 = {1, 2};
    int[] array2 = {1, 2};
    Arrays.equals(array1, array2);  // true
    
    • 1
    • 2
    • 3
    2)value比较时的一个情况

    值value 和 基本类型比较

    先说一种比较常见的情况,我们在创建一个 HashMap对象为 map1后,有时会需要比较 HashMap中是否包含某个值,一般使用 containsValue() 判断是否数据包含在 HashMap中(containsValue()的使用和 containsKey()类似)。比如

    Map map1 = new HashMap<>();
    map1.put('a', 1);
    map1.put('b', 200);
    // 一般使用 containsValue() 判断是否数据包含在 HashMap 中。比如
    System.out.println(map1.containsValue(1));     // true
    System.out.println(map1.containsValue(200));     // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    为了说明下一步的例子,这里使用 == 进行比较,给出比较的结果比如

    Map map1 = new HashMap<>();
    map1.put('a', 1);
    map1.put('b', 200);
    // 但是为了说明下面的例子,这里使用 == 进行比较,比如
    System.out.println(map1.get('a') == 1);    // true
    System.out.println(map1.get('b') == 200);   // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里,map.get()得到的数据类型是Integer,在和基本类型 int比较时,相当于两个基本类型int比较,比的是值是否相等。

    值value 和 包装类型比较

    下面说的一种情况,我不知道什么时候用到是合适的,一个朋友遇到时,也是挺想不到的。

    创建一个 HashMap对象为 map1,存储一些数据。

    创建另一个 HashMap对象为 map2,存储和map1相同的数据。

    Map map1 = new HashMap<>();
    map1.put('a', 1);
    map1.put('b', 200);
    // 创建另一个 HashMap ,存储相同的数据
    Map map2 = new HashMap<>();
    map2.put('a', 1);
    map2.put('b', 200);
    // 比较
    System.out.println(map2.get('a') == map1.get('a'));    // true
    System.out.println(map2.get('b') == map1.get('b'));    // false
    // 如果要比较,需要使用 equals() 
    System.out.println(map2.get('a').equals(map1.get('a')));    // true
    System.out.println(map2.get('b').equals(map1.get('b')));    // true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里出现这样的结果,是一个对象和一个对象的比较(本章2.1),比较的是map中存储对象的地址是否相等。

  • 相关阅读:
    C语言详解系列——操作符详解
    Covalent Network(CQT)构建 Web3 最大的结构化数据集,开拓AI、安全性和数据质量的融合
    国内有好用的RPA浏览器吗?RPA浏览器的作用是什么?
    基于形状的匹配突破(黑中白,曙光,追赶visionpro)
    【STM32外设系列】JW01三合一空气质量检测模块
    Python获取命令行参数的方法sys.argv
    【javaEE】多线程初阶(Part7定时器)!!
    leetcode面试题0808有重复字符串的排列组合
    台式电脑连不上wifi怎么办
    深入理解JVM - 内存区域介绍(抄录整理)
  • 原文地址:https://blog.csdn.net/qq_35439539/article/details/126408309