首页 > 编程语言 >Java如何判断两个字符串是否相等?

Java如何判断两个字符串是否相等?

时间:2023-02-19 13:59:14浏览次数:33  
标签:相等 Java String equals new 字符串 return 小萝莉

“这个问题也可以引申为 .equals() 和 ‘==’ 操作符有什么区别。”

.equals() 就好像我们普通人,看见阿丽塔以为是洛丽塔,看见洛丽塔以为是阿丽塔,看起来一样就觉得她们是同一个人;“==”操作符就好像她们的妈妈,要求更严格,观察更细致,一眼就能分辨出谁是姐姐谁是妹妹。

String alita = new String("小萝莉");
String luolita = new String("小萝莉");

System.out.println(alita.equals(luolita)); // true
System.out.println(alita == luolita); // false

就上面这段代码来说,.equals() 输出的结果为 true,而“==”操作符输出的结果为 false——前者要求内容相等就可以,后者要求必须是同一个对象。

“三妹,之前已经学过了,Java 的所有类都默认地继承 Object 这个超类,该类有一个名为 .equals() 的方法。”一边说,我一边打开了 Object 类的源码。

public boolean equals(Object obj) {
   return (this == obj);
}

你看,Object 类的 .equals() 方法默认采用的是“==”操作符进行比较。假如子类没有重写该方法的话,那么“==”操作符和 .equals() 方法的功效就完全一样——比较两个对象的内存地址是否相等。

但实际情况中,有不少类重写了 .equals() 方法,因为比较内存地址的要求比较严格,不太符合现实中所有的场景需求。拿 String 类来说,我们在比较字符串的时候,的确只想判断它们俩的内容是相等的就可以了,并不想比较它们俩是不是同一个对象。

况且,字符串有字符串常量池的概念,本身就推荐使用 String s = "字符串" 这种形式来创建字符串对象,而不是通过 new 关键字的方式,因为可以把字符串缓存在字符串常量池中,方便下次使用。

“那就来看一下 .equals() 方法的源码吧。”我说。

public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
  }
   if (anObject instanceof String) {
       String aString = (String)anObject;
       if (coder() == aString.coder()) {
           return isLatin1() ? StringLatin1.equals(value, aString.value)
                  : StringUTF16.equals(value, aString.value);
      }
  }
   return false;
}

首先,如果两个字符串对象的可以“==”,那就直接返回 true 了,因为这种情况下,字符串内容是必然相等的。否则就按照字符编码进行比较,分为 UTF16 和 Latin1,差别不是很大,就拿 Latin1 的来说吧。

@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
   if (value.length == other.length) {
       for (int i = 0; i < value.length; i++) {
           if (value[i] != other[i]) {
               return false;
          }
      }
       return true;
  }
   return false;
}

我的 JDK 版本是 Java 17,也就是最新的 LTS(长期支持)版本。该版本中,String 类使用字节数组实现的,所以比较两个字符串的内容是否相等时,可以先比较字节数组的长度是否相等,不相等就直接返回 false;否则就遍历两个字符串的字节数组,只有有一个字节不相等,就返回 false。

第一题:

new String("小萝莉").equals("小萝莉")

“输出什么呢?”我问。

.equals() 比较的是两个字符串对象的内容是否相等,所以结果为 true。”三妹答。

第二题:

new String("小萝莉") == "小萝莉"

“==操作符左侧的是在堆中创建的对象,右侧是在字符串常量池中的对象,尽管内容相同,但内存地址不同,所以返回 false。”三妹答。

第三题:

new String("小萝莉") == new String("小萝莉")

“new 出来的对象肯定是完全不同的内存地址,所以返回 false。”三妹答。

第四题:

"小萝莉" == "小萝莉"

“字符串常量池中只会有一个相同内容的对象,所以返回 true。”三妹答。

第五题:

"小萝莉" == "小" + "萝莉"

“由于‘小’和‘萝莉’都在字符串常量池,所以编译器在遇到‘+’操作符的时候将其自动优化为“小萝莉”,所以返回 true。”

第六题:

new String("小萝莉").intern() == "小萝莉"

new String("小萝莉") 在执行的时候,会先在字符串常量池中创建对象,然后再在堆中创建对象;执行 intern() 方法的时候发现字符串常量池中已经有了‘小萝莉’这个对象,所以就直接返回字符串常量池中的对象引用了,那再与字符串常量池中的‘小萝莉’比较,当然会返回 true 了。”三妹说。

“如果要进行两个字符串对象的内容比较,除了 .equals() 方法,还有其他两个可选的方案。”

1)Objects.equals()

Objects.equals() 这个静态方法的优势在于不需要在调用之前判空。

public static boolean equals(Object a, Object b) {
   return (a == b) || (a != null && a.equals(b));
}

如果直接使用 a.equals(b),则需要在调用之前对 a 进行判空,否则可能会抛出空指针 java.lang.NullPointerExceptionObjects.equals() 用起来就完全没有这个担心。

Objects.equals("小萝莉", new String("小" + "萝莉")) // --> true
Objects.equals(null, new String("小" + "萝莉")); // --> false
Objects.equals(null, null) // --> true

String a = null;
a.equals(new String("小" + "萝莉")); // throw exception

2)String 类的 .contentEquals()

.contentEquals() 的优势在于可以将字符串与任何的字符序列(StringBuffer、StringBuilder、String、CharSequence)进行比较。

public boolean contentEquals(CharSequence cs) {
   // Argument is a StringBuffer, StringBuilder
   if (cs instanceof AbstractStringBuilder) {
       if (cs instanceof StringBuffer) {
           synchronized(cs) {
               return nonSyncContentEquals((AbstractStringBuilder)cs);
          }
      } else {
           return nonSyncContentEquals((AbstractStringBuilder)cs);
      }
  }
   // Argument is a String
   if (cs instanceof String) {
       return equals(cs);
  }
   // Argument is a generic CharSequence
   int n = cs.length();
   if (n != length()) {
       return false;
  }
   byte[] val = this.value;
   if (isLatin1()) {
       for (int i = 0; i < n; i++) {
           if ((val[i] & 0xff) != cs.charAt(i)) {
               return false;
          }
      }
  } else {
       if (!StringUTF16.contentEquals(val, cs, n)) {
           return false;
      }
  }
   return true;
}

从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化;如果是 String 的话,其实调用的还是 equals() 方法。当然了,这也就意味着使用该方法进行比较的时候,多出来了很多步骤,性能上有些损失。

“是的,总体上感觉还是 Objects.equals() 比较舒服。”三妹的眼睛是雪亮的,发现了这个方法的优点。

标签:相等,Java,String,equals,new,字符串,return,小萝莉
From: https://www.cnblogs.com/chaosssock/p/17134635.html

相关文章

  • Java字符串拼接
    “哥,你让我看的《Java开发手册openinnewwindow》上有这么一段内容:循环体内,拼接字符串最好使用StringBuilder的append()方法,而不是+号操作符。这是为什么呀?”三妹......
  • Java字符串分割
    “哥,我感觉字符串拆分没什么可讲的呀,直接上String类的split()方法不就可以了!”三妹毫不客气地说。“假如你真的这么觉得,那可要注意了,事情远没这么简单。”我微笑着说......
  • 漏洞分析-log4j RCE-JAVA篇
    0x00原理分析log4j的介绍:log4j是java打印输出日志的一个API,只要引入了log4j的jar包或者是在xml配置文件内配置好log4j即可输入java运行时产生的日志内容,一般用于记录网......
  • 【LeeCode】剑指 Offer 58 - II. 左旋转字符串
    【题目描述】字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回......
  • Java入门博客
    开始准备 新建一个file->new->project->emptyproject   新建一个file->new->module->java 打开projectstructure,修改这两处   修改注释的颜色 ......
  • Java基础知识点(带返回值方法的定义和调用
    一:带返回值方法的定义方法的返回值其实就是方法运行的最终结果。如果要在调用处根据方法的结果,去编写另外一段逻辑,为了在调用处拿到方法的结果,就需要定义带返回值的方法。eg......
  • LeetCode-53. 最大子数组和(Java)
    一、前言:......
  • Java:使用thumbnailator实现图片压缩处理
    thumbnailator可以实现图片的压缩、旋转、添加水印文档https://github.com/coobird/thumbnailatorhttps://github.com/coobird/thumbnailator/wiki/Examples依赖<!--......
  • Java内存
    系统中的堆、栈和数据结构堆、栈系统中的堆、栈和数据结构堆、栈不是一个概念。可以说系统中的堆、栈是真实的内存物理区,数据结构中的堆、栈是抽象的数据存储结构。数......
  • java序列化反序列化
    序列化概述序列化:将数据结构或对象转换成二进制字节流的过程反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程TCP/IP四层模型transient......