<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          equals 和 == 之戰(zhàn)

          共 7732字,需瀏覽 16分鐘

           ·

          2021-06-13 02:14

          “哥,如何比較兩個字符串相等啊?”三妹問。

          “這個問題看似簡單,卻在 Stack Overflow 上有超過 370 萬的訪問量?!蔽艺f,“這個問題也可以引申為 .equals() 和 ‘==’ 操作符有什么區(qū)別。”

          • “==”操作符用于比較兩個對象的地址是否相等。
          • .equals() 方法用于比較兩個對象的內(nèi)容是否相等。

          “不是很理解?!比酶械胶芾Щ?。

          “我來舉個不恰當(dāng)又很恰當(dāng)?shù)睦?,一看你就明白了,三妹。?/p>

          有一對雙胞胎,姐姐叫阿麗塔,妹妹叫洛麗塔。我們普通人可能完全無法分辨誰是姐姐誰是妹妹,可她們的媽媽卻可以輕而易舉地辨認出。

          .equals() 就好像我們普通人,看見阿麗塔以為是洛麗塔,看見洛麗塔以為是阿麗塔,看起來一樣就覺得她們是同一個人;“==”操作符就好像她們的媽媽,要求更嚴格,觀察更細致,一眼就能分辨出誰是姐姐誰是妹妹。

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

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

          就上面這段代碼來說,.equals() 輸出的結(jié)果為 true,而“==”操作符輸出的結(jié)果為 false——前者要求內(nèi)容相等就可以,后者要求必須是同一個對象。

          “三妹,之前已經(jīng)學(xué)過了,Java 的所有類都默認地繼承 Object 這個超類,該類有一個名為 .equals() 的方法。”一邊說,我一邊打開了 Object 類的源碼。

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

          你看,Object 類的 .equals() 方法默認采用的是“==”操作符進行比較。假如子類沒有重寫該方法的話,那么“==”操作符和 .equals() 方法的功效就完全一樣——比較兩個對象的內(nèi)存地址是否相等。

          但實際情況中,有不少類重寫了 .equals() 方法,因為比較內(nèi)存地址的要求比較嚴格,不太符合現(xiàn)實中所有的場景需求。拿 String 類來說,我們在比較字符串的時候,的確只想判斷它們倆的內(nèi)容是相等的就可以了,并不想比較它們倆是不是同一個對象。

          況且,字符串有字符串常量池的概念,本身就推薦使用 String s = "字符串" 這種形式來創(chuàng)建字符串對象,而不是通過 new 關(guān)鍵字的方式,因為可以把字符串緩存在字符串常量池中,方便下次使用。

          “哦,我明白了。”三妹說。

          “那就來看一下 .equals() 方法的源碼吧?!蔽艺f。

          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 了,因為這種情況下,字符串內(nèi)容是必然相等的。否則就按照字符編碼進行比較,分為 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 11,也就是最新的 LTS(長期支持)版本。該版本中,String 類使用字節(jié)數(shù)組實現(xiàn)的,所以比較兩個字符串的內(nèi)容是否相等時,可以先比較字節(jié)數(shù)組的長度是否相等,不相等就直接返回 false;否則就遍歷兩個字符串的字節(jié)數(shù)組,只有有一個字節(jié)不相等,就返回 false。

          “嗯,二哥,這段源碼不難理解?!比米孕诺卣f。

          “那出幾道題考考你吧!”我說。

          第一題:

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

          “輸出什么呢?”我問。

          .equals() 比較的是兩個字符串對象的內(nèi)容是否相等,所以結(jié)果為 true?!比么?。

          第二題:

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

          “==操作符左側(cè)的是在堆中創(chuàng)建的對象,右側(cè)是在字符串常量池中的對象,盡管內(nèi)容相同,但內(nèi)存地址不同,所以返回 false?!比么?。

          第三題:

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

          “new 出來的對象肯定是完全不同的內(nèi)存地址,所以返回 false?!比么?。

          第四題:

          "小蘿莉" == "小蘿莉"

          “字符串常量池中只會有一個相同內(nèi)容的對象,所以返回 true?!比么?。

          第五題:

          "小蘿莉" == "小" + "蘿莉"

          “由于‘小’和‘蘿莉’都在字符串常量池,所以編譯器在遇到‘+’操作符的時候?qū)⑵渥詣觾?yōu)化為“小蘿莉”,所以返回 true?!?/p>

          第六題:

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

          new String("小蘿莉") 在執(zhí)行的時候,會先在字符串常量池中創(chuàng)建對象,然后再在堆中創(chuàng)建對象;執(zhí)行 intern() 方法的時候發(fā)現(xiàn)字符串常量池中已經(jīng)有了‘小蘿莉’這個對象,所以就直接返回字符串常量池中的對象引用了,那再與字符串常量池中的‘小蘿莉’比較,當(dāng)然會返回 true 了?!比谜f。

          哇,不得不說,三妹前幾節(jié)的字符串相關(guān)內(nèi)容都完全學(xué)會了呀!

          “三妹,哥再給你補充一點?!蔽艺f。

          “如果要進行兩個字符串對象的內(nèi)容比較,除了 .equals() 方法,還有其他兩個可選的方案?!?/p>

          1)Objects.equals()

          Objects.equals() 這個靜態(tài)方法的優(yōu)勢在于不需要在調(diào)用之前判空。

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

          如果直接使用 a.equals(b),則需要在調(diào)用之前對 a 進行判空,否則可能會拋出空指針 java.lang.NullPointerExceptionObjects.equals() 用起來就完全沒有這個擔(dān)心。

          Objects.equals("小蘿莉"new String("小" + "蘿莉")) // --> true
          Objects.equals(nullnew String("小" + "蘿莉")); // --> false
          Objects.equals(nullnull// --> true

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

          2)String 類的 .contentEquals()

          .contentEquals() 的優(yōu)勢在于可以將字符串與任何的字符序列(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 的話,其實調(diào)用的還是 equals() 方法。當(dāng)然了,這也就意味著使用該方法進行比較的時候,多出來了很多步驟,性能上有些損失。

          “是的,總體上感覺還是 Objects.equals() 比較舒服?!比玫难劬κ茄┝恋?,發(fā)現(xiàn)了這個方法的優(yōu)點。

          ---未完待續(xù),期待下集---

          點擊「閱讀原文」可直達《教妹學(xué)Java》專欄的在線閱讀地址!

          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人干人人莫 | 97免费超碰 | 18黄网站 | 天天操天天干天天操天天干 | 热草免费在线 |