
百度面試有個經典環(huán)節(jié)白板手寫equals??雌饋砗唵慰墒畟€人里有六個寫出來能編譯放進 HashMap 卻怎么都get不出來。面試官掃一眼就知道了——你只重寫了equals忘了hashCode。這道題考的不是 API是你對對象相等這件事在 JVM 和散列表里到底怎么運作的理解。今天拆成五組把約定、坑、底層一次講透。一、 到底比的是什么面試官先說和equals區(qū)別候選人標準答法比地址equals比值。深度解析這句話對但得拆開?;绢愋蚷nt、long、boolean…比的是值本身。引用類型比的是棧里存的引用是否指向同一個堆對象即內存地址相同與內容無關。equals是Object的方法默認實現(xiàn)就是public boolean equals(Object obj) { return (this obj); }所以不重寫的話equals和完全等價。String 之所以能比值是因為它重寫了equals先比地址再比長度再逐字符比。普通答法 vs 高分答法普通 比地址equals 比值。高分我會強調對基本類型 比值、對引用類型 比地址并點出Object.equals 默認就是 這個事實。順帶補Kotlin 的才是 Java 的比地址在 Kotlin 里自動調用equals還可空安全——這層語言差異一提考官知道你跨語言有體感。二、equals 與 hashCode 的硬約定本篇第一道核心問答是整個散列體系的基石。面試官重寫 equals 時hashCode 也要重寫為什么深度解析這是Object類的契約Java 語言規(guī)范明寫如果兩個對象根據equals(Object)相等那么調用這兩個對象的hashCode()必須產生相同的整數結果。注意反向不成立hashCode 相同equals 不一定相等哈希碰撞。為什么有這條約定因為hashCode的唯一使命是服務散列表HashMap、HashSet、Hashtable。約定保證了相等的對象必須落在同一個桶里否則基于 equals 的查找就崩了。高分答法我會把契約二字拎出來——這不是建議是規(guī)范。違反它不會編譯報錯但會在運行時埋雷而且雷只在你把對象塞進 HashSet/HashMap 時才爆。這種編譯通過、運行抽風的 bug 最難查。三、只重寫 equals 不重寫 hashCode 會怎樣本篇最關鍵的深挖也是白板題翻車點。面試官那我只重寫 equals不寫 hashCode放 HashMap 會出啥事深度解析災難性的存得進、取不出。看代碼data class User(val id: Int, val name: String) { override fun equals(other: Any?): Boolean { // 只重寫了 equals if (other !is User) return false return id other.id } // 沒重寫 hashCode } val map HashMapUser, String() val u1 User(1, 張三) map[u1] vip println(map[u1]) // 大概率 vip還是 null如果User沒重寫hashCode它繼承自Object的默認實現(xiàn)——基于對象內存地址的身份哈希。問題來了1.put(u1)時用u1的地址哈希算桶存進去。2. 你哪怕 new 一個u2 User(1,張三)u2.equals(u1)為 true因為重寫了 equals但u2.hashCode()是 u2 的地址哈希和 u1 不同。3.map[u2]去另一個桶找找不到 → 返回 null。更隱蔽就算用同一個u1去get表面看沒問題但只要中間有任何環(huán)節(jié)用了內容相等但引用不同的對象做 key就徹底失聯(lián)。普通答法 vs 高分答法普通會出問題取不出來。高分我會完整復述put 用原 hashCode 入桶、get 用 key 的 hashCode 找桶兩個對象 equals 相等但 hashCode 不同就落不同桶的鏈路并點出默認hashCode是身份哈希。這條講清楚面試官就知道你真 debug 過。四、HashMap 為什么用 hashCode 定位桶本篇第三道核心問答橫向拉到容器底層。面試官HashMap 為啥非得用 hashCode 定位直接 equals 比不行嗎深度解析因為要 O(1)。HashMap 底層是數組 鏈表/紅黑樹。存元素時1. 算key.hashCode()2. 經過擾動函數Java 8(h key.hashCode()) ^ (h 16)打散高位3. 與數組長度取模定位桶(n - 1) hash。這樣無論表里有 1 個還是 10 萬個元素定位桶都是一次位運算常數時間。如果不用哈希、純靠equals遍歷比那就是 O(n)散列表失去意義。// Java 8 HashMap.putVal 關鍵一步 if ((p tab[i (n - 1) hash]) null) // 直接位運算定位 tab[i] newNode(hash, key, value, null);補充hashCode 分布越均勻碰撞越少鏈表越短查詢越快。這就是為什么好的hashCode要讓字段都參與運算IDE 自動生成或Objects.hash(id, name)就是干這個。高分答法我會提擾動函數的作用——只取 hashCode 低位會和數組長度耦合高位信息浪費擾動把高位攪進低位減少碰撞。這展示你不止會用 Map還看過putVal源碼。五、開放追問重寫的坑與最佳實踐面試官那實際項目里重寫有啥坑深度解析兩個高頻雷區(qū)。雷區(qū)一用可變字段做 equals/hashCode。val s mutableSetOf(User(1, 張三)) users.add(u) u.name 李四 // 改了參與 hashCode 的字段 println(users.contains(u)) // false元素丟失因為它在新的桶里HashSet 里元素的 hashCode 變了的瞬間它就既不在舊桶也不在新桶的邏輯位置集合徹底找不到它還造成內存泄漏。雷區(qū)二Kotlin 的data class自動生成。好消息是data class會自動基于主構造函數所有屬性生成equalshashCode雙雙對齊契約天然滿足。但上面可變字段的坑對 data class 同樣存在——所以參與相等的字段盡量用val。最佳實踐用 IDE 一鍵生成或Objects.hash(field1, field2)保證equals和hashCode用同一組字段參與相等的字段保持不可變val/final。高分答法我會補一句——Android 里Bundle、Intent傳對象、RecyclerView的DiffUtil.ItemCallback都依賴正確的 equals寫錯一個數據就錯亂這比 HashMap 取不出更隱蔽。收尾幾點能落地的提醒equals 和 hashCode 這道題百度考的是你有沒有被散列表坑過。只重寫一個、用可變字段、字段不一致——這三個錯新手幾乎必踩其一。面試 Tips具體答法被問區(qū)別先分基本類型/引用類型講再講Object.equals默認就是。說約定務必背出相等對象必須同 hashCode反之不成立。講坑完整復述put 入桶、get 找桶hashCode 不同就失聯(lián)的鏈路。提 HashMap帶出擾動函數和(n-1)hash位運算直接拉到源碼層。我的看法這題答得好不好能看出一個人寫沒寫過生產級 Model 類。Android 里每個data class背后都是這套契約搞懂它序列化、去重、緩存全都不會再翻車。評論區(qū)聊聊你有沒有因為 equals/hashCode 寫錯導致過一個查不出來的詭異 bug---點贊、在看、轉發(fā)三連是對我最大的支持「Android 大廠面經·從入門到精通」連載系列上一篇第003篇 快手·Android 開發(fā)面試——StringBuilder 和 StringBuffer 選錯一個下一篇預告第005篇 京東·Android 面試——接口和抽象類的區(qū)別評論區(qū)聊聊你面試遇到過最難的 Android 問題是哪道關于本系列「Android 大廠面經·從入門到精通」是360篇連載系列覆蓋美團、字節(jié)跳動、阿里巴巴、快手、百度、京東、華為、小米等30家公司從 Java 基礎到架構師終面的真實面試內容。本篇屬于階段1·入門篇——Java 語言基礎。